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/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