blob: ecf900ec2e5b9d47008cf48c751c8bb4cbd167d9 [file] [log] [blame]
Matthew Barth17d1fe22017-05-11 15:00:36 -05001#pragma once
2
3#include <algorithm>
Matthew Barth4af419c2017-06-12 13:39:31 -05004#include <numeric>
Matthew Barth17d1fe22017-05-11 15:00:36 -05005
6namespace phosphor
7{
8namespace fan
9{
10namespace control
11{
12namespace action
13{
14
15/**
16 * @brief An action to set the speed on a zone
Matthew Barth861d77c2017-05-22 14:18:25 -050017 * @details The zone is held at the given speed when a defined number of
Matthew Barth17d1fe22017-05-11 15:00:36 -050018 * properties in the group are set to the given state
19 *
20 * @param[in] count - Number of properties
21 * @param[in] state - Value the property(s) needed to be set at
22 * @param[in] speed - Speed to set the zone to
23 *
24 * @return Lambda function
25 * A lambda function to set the zone speed when the number of properties
26 * within the group are at a certain value
27 */
Matthew Barth9e741ed2017-06-02 16:29:09 -050028template <typename T>
29auto count_state_before_speed(size_t count, T&& state, uint64_t speed)
Matthew Barth17d1fe22017-05-11 15:00:36 -050030{
Matthew Barth9e741ed2017-06-02 16:29:09 -050031 return [count,
32 speed,
33 state = std::forward<T>(state)](auto& zone, auto& group)
Matthew Barth17d1fe22017-05-11 15:00:36 -050034 {
35 size_t numAtState = std::count_if(
36 group.begin(),
37 group.end(),
38 [&zone, &state](auto const& entry)
39 {
Matthew Barthbc651602017-08-10 16:59:43 -050040 try
41 {
42 return zone.template getPropertyValue<T>(
43 entry.first,
44 std::get<intfPos>(entry.second),
45 std::get<propPos>(entry.second)) == state;
46 }
47 catch (const std::out_of_range& oore)
48 {
49 // Default to property not equal when not found
50 return false;
51 }
Matthew Barth17d1fe22017-05-11 15:00:36 -050052 });
Matthew Barth17d1fe22017-05-11 15:00:36 -050053 if (numAtState >= count)
54 {
55 zone.setSpeed(speed);
56 }
Matthew Barth60b00762017-08-15 13:39:06 -050057 // Update group's fan control active allowed based on action results
58 zone.setActiveAllow(&group, !(numAtState >= count));
Matthew Barth17d1fe22017-05-11 15:00:36 -050059 };
60}
61
Matthew Barth4af419c2017-06-12 13:39:31 -050062/**
63 * @brief An action to set the floor speed on a zone
64 * @details Based on the average of the defined sensor group values, the floor
65 * speed is selected from the first map key entry that the average sensor value
66 * is less than.
67 *
68 * @param[in] val_to_speed - Ordered map of sensor value-to-speed
69 *
70 * @return Lambda function
71 * A lambda function to set the zone's floor speed when the average of
72 * property values within the group is below the lowest sensor value given
73 */
74auto set_floor_from_average_sensor_value(
75 std::map<int64_t, uint64_t>&& val_to_speed)
76{
77 return [val_to_speed = std::move(val_to_speed)](auto& zone, auto& group)
78 {
79 auto speed = zone.getDefFloor();
80 if (group.size() != 0)
81 {
Matthew Barthbc651602017-08-10 16:59:43 -050082 auto count = 0;
Matthew Barth4af419c2017-06-12 13:39:31 -050083 auto sumValue = std::accumulate(
84 group.begin(),
85 group.end(),
86 0,
Matthew Barthbc651602017-08-10 16:59:43 -050087 [&zone, &count](int64_t sum, auto const& entry)
Matthew Barth4af419c2017-06-12 13:39:31 -050088 {
Matthew Barthbc651602017-08-10 16:59:43 -050089 try
90 {
91 return sum +
92 zone.template getPropertyValue<int64_t>(
93 entry.first,
94 std::get<intfPos>(entry.second),
95 std::get<propPos>(entry.second));
96 }
97 catch (const std::out_of_range& oore)
98 {
99 count++;
100 return sum;
101 }
Matthew Barth4af419c2017-06-12 13:39:31 -0500102 });
Matthew Barthbc651602017-08-10 16:59:43 -0500103 if ((group.size() - count) > 0)
Matthew Barth4af419c2017-06-12 13:39:31 -0500104 {
Matthew Barthbc651602017-08-10 16:59:43 -0500105 auto avgValue = sumValue / (group.size() - count);
106 auto it = std::find_if(
107 val_to_speed.begin(),
108 val_to_speed.end(),
109 [&avgValue](auto const& entry)
110 {
111 return avgValue < entry.first;
112 }
113 );
114 if (it != std::end(val_to_speed))
115 {
116 speed = (*it).second;
117 }
Matthew Barth4af419c2017-06-12 13:39:31 -0500118 }
119 }
120 zone.setFloor(speed);
121 };
122}
123
Matthew Barthe0ca13e2017-06-13 16:29:09 -0500124/**
125 * @brief An action to set the ceiling speed on a zone
126 * @details Based on the average of the defined sensor group values, the ceiling
127 * speed is selected from the map key transition point that the average sensor
128 * value falls within depending on the key values direction from what was
129 * previously read.
130 *
131 * @param[in] val_to_speed - Ordered map of sensor value-to-speed transitions
132 *
133 * @return Lambda function
134 * A lambda function to set the zone's ceiling speed when the average of
135 * property values within the group is above(increasing) or
136 * below(decreasing) the key transition point
137 */
138auto set_ceiling_from_average_sensor_value(
139 std::map<int64_t, uint64_t>&& val_to_speed)
140{
141 return [val_to_speed = std::move(val_to_speed)](auto& zone, auto& group)
142 {
143 auto speed = zone.getCeiling();
144 if (group.size() != 0)
145 {
Matthew Barthbc651602017-08-10 16:59:43 -0500146 auto count = 0;
Matthew Barthe0ca13e2017-06-13 16:29:09 -0500147 auto sumValue = std::accumulate(
148 group.begin(),
149 group.end(),
150 0,
Matthew Barthbc651602017-08-10 16:59:43 -0500151 [&zone, &count](int64_t sum, auto const& entry)
Matthew Barthe0ca13e2017-06-13 16:29:09 -0500152 {
Matthew Barthbc651602017-08-10 16:59:43 -0500153 try
154 {
155 return sum +
156 zone.template getPropertyValue<int64_t>(
157 entry.first,
158 std::get<intfPos>(entry.second),
159 std::get<propPos>(entry.second));
160 }
161 catch (const std::out_of_range& oore)
162 {
163 count++;
164 return sum;
165 }
Matthew Barthe0ca13e2017-06-13 16:29:09 -0500166 });
Matthew Barthbc651602017-08-10 16:59:43 -0500167 if ((group.size() - count) > 0)
168 {
169 auto avgValue = sumValue / (group.size() - count);
170 auto prevValue = zone.swapCeilingKeyValue(avgValue);
171 if (avgValue != prevValue)
172 {// Only check if previous and new values differ
173 if (avgValue < prevValue)
174 {// Value is decreasing from previous
175 for (auto it = val_to_speed.rbegin();
176 it != val_to_speed.rend();
177 ++it)
Matthew Barthe0ca13e2017-06-13 16:29:09 -0500178 {
Matthew Barthbc651602017-08-10 16:59:43 -0500179 if (it == val_to_speed.rbegin() &&
180 avgValue >= it->first)
181 {
182 // Value is at/above last map key, set
183 // ceiling speed to the last map key's value
184 speed = it->second;
185 break;
186 }
187 else if (std::next(it, 1) == val_to_speed.rend() &&
188 avgValue <= it->first)
189 {
190 // Value is at/below first map key, set
191 // ceiling speed to the first map key's value
192 speed = it->second;
193 break;
194 }
195 if (avgValue < it->first &&
196 it->first <= prevValue)
197 {
198 // Value decreased & transitioned across
199 // a map key, update ceiling speed to this
200 // map key's value when new value is below
201 // map's key and the key is at/below the
202 // previous value
203 speed = it->second;
204 }
Matthew Barthe0ca13e2017-06-13 16:29:09 -0500205 }
206 }
Matthew Barthbc651602017-08-10 16:59:43 -0500207 else
208 {// Value is increasing from previous
209 for (auto it = val_to_speed.begin();
210 it != val_to_speed.end();
211 ++it)
Matthew Barthe0ca13e2017-06-13 16:29:09 -0500212 {
Matthew Barthbc651602017-08-10 16:59:43 -0500213 if (it == val_to_speed.begin() &&
214 avgValue <= it->first)
215 {
216 // Value is at/below first map key, set
217 // ceiling speed to the first map key's value
218 speed = it->second;
219 break;
220 }
221 else if (std::next(it, 1) == val_to_speed.end() &&
222 avgValue >= it->first)
223 {
224 // Value is at/above last map key, set
225 // ceiling speed to the last map key's value
226 speed = it->second;
227 break;
228 }
229 if (avgValue > it->first &&
230 it->first >= prevValue)
231 {
232 // Value increased & transitioned across
233 // a map key, update ceiling speed to this
234 // map key's value when new value is above
235 // map's key and the key is at/above the
236 // previous value
237 speed = it->second;
238 }
Matthew Barthe0ca13e2017-06-13 16:29:09 -0500239 }
240 }
241 }
242 }
243 }
244 zone.setCeiling(speed);
245 };
246}
247
Matthew Barth24623522017-06-21 14:09:57 -0500248/**
249 * @brief An action to set the speed increase delta and request speed change
250 * @details Provides the ability to determine what the net increase delta the
251 * zone's fan speeds should be updated by from their current target speed and
252 * request that new target speed.
253 *
254 * @param[in] state - State to compare the group's property value to
Matthew Barth172f3932017-08-14 11:07:46 -0500255 * @param[in] factor - Factor to apply to the calculated net delta
Matthew Barth24623522017-06-21 14:09:57 -0500256 * @param[in] speedDelta - Speed delta of the group
257 *
258 * @return Lambda function
259 * A lambda function that determines the net increase delta and requests
260 * a new target speed with that increase for the zone.
261 */
262template <typename T>
Matthew Barth172f3932017-08-14 11:07:46 -0500263auto set_net_increase_speed(T&& state, T&& factor, uint64_t speedDelta)
Matthew Barth24623522017-06-21 14:09:57 -0500264{
265 return [speedDelta,
Matthew Barth172f3932017-08-14 11:07:46 -0500266 factor = std::forward<T>(factor),
Matthew Barth24623522017-06-21 14:09:57 -0500267 state = std::forward<T>(state)](auto& zone, auto& group)
268 {
269 auto netDelta = zone.getIncSpeedDelta();
270 std::for_each(
271 group.begin(),
272 group.end(),
Matthew Barth172f3932017-08-14 11:07:46 -0500273 [&zone, &state, &factor, &speedDelta, &netDelta](
Matthew Barthbc651602017-08-10 16:59:43 -0500274 auto const& entry)
Matthew Barth24623522017-06-21 14:09:57 -0500275 {
Matthew Barthbc651602017-08-10 16:59:43 -0500276 try
Matthew Barth24623522017-06-21 14:09:57 -0500277 {
Matthew Barthbc651602017-08-10 16:59:43 -0500278 T value = zone.template getPropertyValue<T>(
279 entry.first,
280 std::get<intfPos>(entry.second),
281 std::get<propPos>(entry.second));
282 // TODO openbmc/phosphor-fan-presence#7 - Support possible
283 // state types for comparison
284 if (value >= state)
285 {
Matthew Barth172f3932017-08-14 11:07:46 -0500286 // Increase by at least a single delta(factor)
Matthew Barthbc651602017-08-10 16:59:43 -0500287 // to attempt bringing under 'state'
Matthew Barth172f3932017-08-14 11:07:46 -0500288 auto delta = std::max(
289 (value - state),
290 factor);
291 // Increase is the factor applied to the
292 // difference times the given speed delta
293 netDelta = std::max(
294 netDelta,
295 (delta/factor) * speedDelta);
Matthew Barthbc651602017-08-10 16:59:43 -0500296 }
297 }
298 catch (const std::out_of_range& oore)
299 {
300 // Property value not found, netDelta unchanged
Matthew Barth24623522017-06-21 14:09:57 -0500301 }
302 }
303 );
Matthew Barth240397b2017-06-22 11:23:30 -0500304 // Request speed change for target speed update
305 zone.requestSpeedIncrease(netDelta);
Matthew Barth24623522017-06-21 14:09:57 -0500306 };
307}
308
Matthew Barth0ce99d82017-06-22 15:07:29 -0500309/**
310 * @brief An action to set the speed decrease delta and request speed change
311 * @details Provides the ability to determine what the net decrease delta each
312 * zone's fan speeds should be updated by from their current target speed, and
313 * request that speed change occur on the next decrease interval.
314 *
315 * @param[in] state - State to compare the group's property value to
Matthew Barth172f3932017-08-14 11:07:46 -0500316 * @param[in] factor - Factor to apply to the calculated net delta
Matthew Barth0ce99d82017-06-22 15:07:29 -0500317 * @param[in] speedDelta - Speed delta of the group
318 *
319 * @return Lambda function
320 * A lambda function that determines the net decrease delta and requests
321 * a new target speed with that decrease for the zone.
322 */
323template <typename T>
Matthew Barth172f3932017-08-14 11:07:46 -0500324auto set_net_decrease_speed(T&& state, T&& factor, uint64_t speedDelta)
Matthew Barth0ce99d82017-06-22 15:07:29 -0500325{
326 return [speedDelta,
Matthew Barth172f3932017-08-14 11:07:46 -0500327 factor = std::forward<T>(factor),
Matthew Barth0ce99d82017-06-22 15:07:29 -0500328 state = std::forward<T>(state)](auto& zone, auto& group)
329 {
330 auto netDelta = zone.getDecSpeedDelta();
331 std::for_each(
332 group.begin(),
333 group.end(),
Matthew Barth172f3932017-08-14 11:07:46 -0500334 [&zone, &state, &factor, &speedDelta, &netDelta](auto const& entry)
Matthew Barth0ce99d82017-06-22 15:07:29 -0500335 {
Matthew Barthbc651602017-08-10 16:59:43 -0500336 try
Matthew Barth0ce99d82017-06-22 15:07:29 -0500337 {
Matthew Barthbc651602017-08-10 16:59:43 -0500338 T value = zone.template getPropertyValue<T>(
339 entry.first,
340 std::get<intfPos>(entry.second),
341 std::get<propPos>(entry.second));
342 // TODO openbmc/phosphor-fan-presence#7 - Support possible
343 // state types for comparison
344 if (value < state)
Matthew Barth0ce99d82017-06-22 15:07:29 -0500345 {
Matthew Barthbc651602017-08-10 16:59:43 -0500346 if (netDelta == 0)
347 {
Matthew Barth172f3932017-08-14 11:07:46 -0500348 netDelta = ((state - value)/factor) * speedDelta;
Matthew Barthbc651602017-08-10 16:59:43 -0500349 }
350 else
351 {
Matthew Barth172f3932017-08-14 11:07:46 -0500352 // Decrease is the factor applied to the
353 // difference times the given speed delta
354 netDelta = std::min(
355 netDelta,
356 ((state - value)/factor) * speedDelta);
Matthew Barthbc651602017-08-10 16:59:43 -0500357 }
Matthew Barth0ce99d82017-06-22 15:07:29 -0500358 }
Matthew Barthbc651602017-08-10 16:59:43 -0500359 }
360 catch (const std::out_of_range& oore)
361 {
362 // Property value not found, netDelta unchanged
Matthew Barth0ce99d82017-06-22 15:07:29 -0500363 }
364 }
365 );
366 // Request speed decrease to occur on decrease interval
367 zone.requestSpeedDecrease(netDelta);
368 };
369}
370
Matthew Barth17d1fe22017-05-11 15:00:36 -0500371} // namespace action
372} // namespace control
373} // namespace fan
374} // namespace phosphor