blob: 0388b5c42ddba02d516a5e525126a94e204de76f [file] [log] [blame]
#include "actions.hpp"
#include "utility.hpp"
namespace phosphor
{
namespace fan
{
namespace control
{
namespace action
{
using namespace phosphor::fan;
Action call_actions_based_on_timer(TimerConf&& tConf,
std::vector<Action>&& actions)
{
return [tConf = std::move(tConf),
actions = std::move(actions)](control::Zone& zone,
const Group& group)
{
try
{
auto it = zone.getTimerEvents().find(__func__);
if (it != zone.getTimerEvents().end())
{
auto& timers = it->second;
auto timerIter = zone.findTimer(group, actions, timers);
if (timerIter == timers.end())
{
// No timer exists yet for action, add timer
zone.addTimer(__func__, group, actions, tConf);
}
else if (timerIter != timers.end())
{
// Remove any timer for this group
timers.erase(timerIter);
if (timers.empty())
{
zone.getTimerEvents().erase(it);
}
}
}
else
{
// No timer exists yet for event, add timer
zone.addTimer(__func__, group, actions, tConf);
}
}
catch (const std::out_of_range& oore)
{
// Group not found, no timers set
}
};
}
void default_floor_on_missing_owner(Zone& zone, const Group& group)
{
// Set/update the services of the group
zone.setServices(&group);
auto services = zone.getGroupServices(&group);
auto defFloor = std::any_of(
services.begin(),
services.end(),
[](const auto& s)
{
return !std::get<hasOwnerPos>(s);
});
if (defFloor)
{
zone.setFloor(zone.getDefFloor());
}
// Update fan control floor change allowed
zone.setFloorChangeAllow(&group, !defFloor);
}
Action set_speed_on_missing_owner(uint64_t speed)
{
return [speed](control::Zone& zone, const Group& group)
{
// Set/update the services of the group
zone.setServices(&group);
auto services = zone.getGroupServices(&group);
auto missingOwner = std::any_of(
services.begin(),
services.end(),
[](const auto& s)
{
return !std::get<hasOwnerPos>(s);
});
if (missingOwner)
{
zone.setSpeed(speed);
}
// Update group's fan control active allowed based on action results
zone.setActiveAllow(&group, !missingOwner);
};
}
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>(
std::get<pathPos>(entry),
std::get<intfPos>(entry),
std::get<propPos>(entry));
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>(
std::get<pathPos>(entry),
std::get<intfPos>(entry),
std::get<propPos>(entry));
}
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>(
std::get<pathPos>(entry),
std::get<intfPos>(entry),
std::get<propPos>(entry));
}
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);
};
}
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
} // namespace phosphor