Create set request speed base action function

This action function is intended to allow the base request speed to be
set to the max value from a group of properties. The base request speed
is the base speed used in requesting a new target speed. By default, the
base request speed is the current target speed.

For example, this action can be used to set the base request speed used
in calculating a new target speed to be based on the max tach feedback
speed from a group of fans instead of the current target speed.

Adding this action function requires the other non-template functions to
also be slightly modified due to linker errors.

Change-Id: Ibbe9b33678b10cb49466174088a587b27bf56d62
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/Makefile.am b/control/Makefile.am
index 7a970ea..09fb1a2 100644
--- a/control/Makefile.am
+++ b/control/Makefile.am
@@ -9,6 +9,7 @@
 	fan.cpp \
 	main.cpp \
 	manager.cpp \
+	actions.cpp \
 	zone.cpp
 
 nodist_phosphor_fan_control_SOURCES = \
diff --git a/control/actions.cpp b/control/actions.cpp
new file mode 100644
index 0000000..a608293
--- /dev/null
+++ b/control/actions.cpp
@@ -0,0 +1,207 @@
+#include "actions.hpp"
+
+namespace phosphor
+{
+namespace fan
+{
+namespace control
+{
+namespace action
+{
+
+using namespace phosphor::fan;
+
+void set_request_speed_base_with_max(control::Zone& zone,
+                                     const Group& group)
+{
+    int64_t base = 0;
+    std::for_each(
+            group.begin(),
+            group.end(),
+            [&zone, &base](auto const& entry)
+        {
+            try
+            {
+                auto value = zone.template getPropertyValue<int64_t>(
+                        entry.first,
+                        std::get<intfPos>(entry.second),
+                        std::get<propPos>(entry.second));
+                base = std::max(base, value);
+            }
+            catch (const std::out_of_range& oore)
+            {
+                // Property value not found, base request speed unchanged
+            }
+        });
+    // A request speed base of 0 defaults to the current target speed
+    zone.setRequestSpeedBase(base);
+}
+
+Action set_floor_from_average_sensor_value(
+        std::map<int64_t, uint64_t>&& val_to_speed)
+{
+    return [val_to_speed = std::move(val_to_speed)](control::Zone& zone,
+                                                    const Group& group)
+    {
+        auto speed = zone.getDefFloor();
+        if (group.size() != 0)
+        {
+            auto count = 0;
+            auto sumValue = std::accumulate(
+                    group.begin(),
+                    group.end(),
+                    0,
+                    [&zone, &count](int64_t sum, auto const& entry)
+                    {
+                        try
+                        {
+                            return sum +
+                                zone.template getPropertyValue<int64_t>(
+                                    entry.first,
+                                    std::get<intfPos>(entry.second),
+                                    std::get<propPos>(entry.second));
+                        }
+                        catch (const std::out_of_range& oore)
+                        {
+                            count++;
+                            return sum;
+                        }
+                    });
+            if ((group.size() - count) > 0)
+            {
+                auto groupSize = static_cast<int64_t>(group.size());
+                auto avgValue = sumValue / (groupSize - count);
+                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);
+    };
+}
+
+Action set_ceiling_from_average_sensor_value(
+        std::map<int64_t, uint64_t>&& val_to_speed)
+{
+    return [val_to_speed = std::move(val_to_speed)](Zone& zone,
+                                                    const Group& group)
+    {
+        auto speed = zone.getCeiling();
+        if (group.size() != 0)
+        {
+            auto count = 0;
+            auto sumValue = std::accumulate(
+                    group.begin(),
+                    group.end(),
+                    0,
+                    [&zone, &count](int64_t sum, auto const& entry)
+                    {
+                        try
+                        {
+                            return sum +
+                                zone.template getPropertyValue<int64_t>(
+                                    entry.first,
+                                    std::get<intfPos>(entry.second),
+                                    std::get<propPos>(entry.second));
+                        }
+                        catch (const std::out_of_range& oore)
+                        {
+                            count++;
+                            return sum;
+                        }
+                    });
+            if ((group.size() - count) > 0)
+            {
+                auto groupSize = static_cast<int64_t>(group.size());
+                auto avgValue = sumValue / (groupSize - count);
+                auto prevValue = zone.swapCeilingKeyValue(avgValue);
+                if (avgValue != prevValue)
+                {// Only check if previous and new values differ
+                    if (avgValue < prevValue)
+                    {// Value is decreasing from previous
+                        for (auto it = val_to_speed.rbegin();
+                             it != val_to_speed.rend();
+                             ++it)
+                        {
+                            if (it == val_to_speed.rbegin() &&
+                                avgValue >= it->first)
+                            {
+                                // Value is at/above last map key, set
+                                // ceiling speed to the last map key's value
+                                speed = it->second;
+                                break;
+                            }
+                            else if (std::next(it, 1) == val_to_speed.rend() &&
+                                     avgValue <= it->first)
+                            {
+                                // Value is at/below first map key, set
+                                // ceiling speed to the first map key's value
+                                speed = it->second;
+                                break;
+                            }
+                            if (avgValue < it->first &&
+                                it->first <= prevValue)
+                            {
+                                // Value decreased & transitioned across
+                                // a map key, update ceiling speed to this
+                                // map key's value when new value is below
+                                // map's key and the key is at/below the
+                                // previous value
+                                speed = it->second;
+                            }
+                        }
+                    }
+                    else
+                    {// Value is increasing from previous
+                        for (auto it = val_to_speed.begin();
+                             it != val_to_speed.end();
+                             ++it)
+                        {
+                            if (it == val_to_speed.begin() &&
+                                avgValue <= it->first)
+                            {
+                                // Value is at/below first map key, set
+                                // ceiling speed to the first map key's value
+                                speed = it->second;
+                                break;
+                            }
+                            else if (std::next(it, 1) == val_to_speed.end() &&
+                                     avgValue >= it->first)
+                            {
+                                // Value is at/above last map key, set
+                                // ceiling speed to the last map key's value
+                                speed = it->second;
+                                break;
+                            }
+                            if (avgValue > it->first &&
+                                it->first >= prevValue)
+                            {
+                                // Value increased & transitioned across
+                                // a map key, update ceiling speed to this
+                                // map key's value when new value is above
+                                // map's key and the key is at/above the
+                                // previous value
+                                speed = it->second;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        zone.setCeiling(speed);
+    };
+}
+
+} // namespace action
+} // namespace control
+} // namespace fan
+} // namespace phosphor
diff --git a/control/actions.hpp b/control/actions.hpp
index ecf900e..3061814 100644
--- a/control/actions.hpp
+++ b/control/actions.hpp
@@ -2,6 +2,8 @@
 
 #include <algorithm>
 #include <numeric>
+#include "types.hpp"
+#include "zone.hpp"
 
 namespace phosphor
 {
@@ -13,6 +15,19 @@
 {
 
 /**
+ * @brief An action to set the request speed base
+ * @details A new target speed is determined using a speed delta being added
+ * or subtracted, for increases or decrease respectively, from a base speed.
+ * This base speed defaults to be the current target speed or is set to a
+ * different base speed(i.e. the fans' tach feedback speed) to request a new
+ * target from.
+ *
+ * @param[in] zone - Zone containing fans
+ * @param[in] group - Group of sensors to determine base from
+ */
+void set_request_speed_base_with_max(Zone& zone, const Group& group);
+
+/**
  * @brief An action to set the speed on a zone
  * @details The zone is held at the given speed when a defined number of
  * properties in the group are set to the given state
@@ -67,183 +82,29 @@
  *
  * @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
+ * @return Action lambda function
+ *     An Action 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 count = 0;
-            auto sumValue = std::accumulate(
-                    group.begin(),
-                    group.end(),
-                    0,
-                    [&zone, &count](int64_t sum, auto const& entry)
-                    {
-                        try
-                        {
-                            return sum +
-                                zone.template getPropertyValue<int64_t>(
-                                    entry.first,
-                                    std::get<intfPos>(entry.second),
-                                    std::get<propPos>(entry.second));
-                        }
-                        catch (const std::out_of_range& oore)
-                        {
-                            count++;
-                            return sum;
-                        }
-                    });
-            if ((group.size() - count) > 0)
-            {
-                auto avgValue = sumValue / (group.size() - count);
-                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);
-    };
-}
+Action set_floor_from_average_sensor_value(
+        std::map<int64_t, uint64_t>&& val_to_speed);
 
 /**
  * @brief An action to set the ceiling speed on a zone
- * @details Based on the average of the defined sensor group values, the ceiling
- * speed is selected from the map key transition point that the average sensor
- * value falls within depending on the key values direction from what was
- * previously read.
+ * @details Based on the average of the defined sensor group values, the
+ * ceiling speed is selected from the map key transition point that the average
+ * sensor value falls within depending on the key values direction from what
+ * was previously read.
  *
  * @param[in] val_to_speed - Ordered map of sensor value-to-speed transitions
  *
- * @return Lambda function
- *     A lambda function to set the zone's ceiling speed when the average of
+ * @return Action lambda function
+ *     An Action function to set the zone's ceiling speed when the average of
  *     property values within the group is above(increasing) or
  *     below(decreasing) the key transition point
  */
-auto set_ceiling_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.getCeiling();
-        if (group.size() != 0)
-        {
-            auto count = 0;
-            auto sumValue = std::accumulate(
-                    group.begin(),
-                    group.end(),
-                    0,
-                    [&zone, &count](int64_t sum, auto const& entry)
-                    {
-                        try
-                        {
-                            return sum +
-                                zone.template getPropertyValue<int64_t>(
-                                    entry.first,
-                                    std::get<intfPos>(entry.second),
-                                    std::get<propPos>(entry.second));
-                        }
-                        catch (const std::out_of_range& oore)
-                        {
-                            count++;
-                            return sum;
-                        }
-                    });
-            if ((group.size() - count) > 0)
-            {
-                auto avgValue = sumValue / (group.size() - count);
-                auto prevValue = zone.swapCeilingKeyValue(avgValue);
-                if (avgValue != prevValue)
-                {// Only check if previous and new values differ
-                    if (avgValue < prevValue)
-                    {// Value is decreasing from previous
-                        for (auto it = val_to_speed.rbegin();
-                             it != val_to_speed.rend();
-                             ++it)
-                        {
-                            if (it == val_to_speed.rbegin() &&
-                                avgValue >= it->first)
-                            {
-                                // Value is at/above last map key, set
-                                // ceiling speed to the last map key's value
-                                speed = it->second;
-                                break;
-                            }
-                            else if (std::next(it, 1) == val_to_speed.rend() &&
-                                     avgValue <= it->first)
-                            {
-                                // Value is at/below first map key, set
-                                // ceiling speed to the first map key's value
-                                speed = it->second;
-                                break;
-                            }
-                            if (avgValue < it->first &&
-                                it->first <= prevValue)
-                            {
-                                // Value decreased & transitioned across
-                                // a map key, update ceiling speed to this
-                                // map key's value when new value is below
-                                // map's key and the key is at/below the
-                                // previous value
-                                speed = it->second;
-                            }
-                        }
-                    }
-                    else
-                    {// Value is increasing from previous
-                        for (auto it = val_to_speed.begin();
-                             it != val_to_speed.end();
-                             ++it)
-                        {
-                            if (it == val_to_speed.begin() &&
-                                avgValue <= it->first)
-                            {
-                                // Value is at/below first map key, set
-                                // ceiling speed to the first map key's value
-                                speed = it->second;
-                                break;
-                            }
-                            else if (std::next(it, 1) == val_to_speed.end() &&
-                                     avgValue >= it->first)
-                            {
-                                // Value is at/above last map key, set
-                                // ceiling speed to the last map key's value
-                                speed = it->second;
-                                break;
-                            }
-                            if (avgValue > it->first &&
-                                it->first >= prevValue)
-                            {
-                                // Value increased & transitioned across
-                                // a map key, update ceiling speed to this
-                                // map key's value when new value is above
-                                // map's key and the key is at/above the
-                                // previous value
-                                speed = it->second;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        zone.setCeiling(speed);
-    };
-}
+Action set_ceiling_from_average_sensor_value(
+        std::map<int64_t, uint64_t>&& val_to_speed);
 
 /**
  * @brief An action to set the speed increase delta and request speed change