blob: ebdff60c8217d8d810156e8c28c8bb4365324ee9 [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 Barth861d77c2017-05-22 14:18:25 -050053 // Update group's fan control active allowed based on action results
54 zone.setActiveAllow(&group, !(numAtState >= count));
Matthew Barth17d1fe22017-05-11 15:00:36 -050055 if (numAtState >= count)
56 {
57 zone.setSpeed(speed);
58 }
59 };
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
255 * @param[in] speedDelta - Speed delta of the group
256 *
257 * @return Lambda function
258 * A lambda function that determines the net increase delta and requests
259 * a new target speed with that increase for the zone.
260 */
261template <typename T>
262auto set_net_increase_speed(T&& state, uint64_t speedDelta)
263{
264 return [speedDelta,
265 state = std::forward<T>(state)](auto& zone, auto& group)
266 {
Matthew Barthbc651602017-08-10 16:59:43 -0500267 T singleDelta = 1;
Matthew Barth24623522017-06-21 14:09:57 -0500268 auto netDelta = zone.getIncSpeedDelta();
269 std::for_each(
270 group.begin(),
271 group.end(),
Matthew Barthbc651602017-08-10 16:59:43 -0500272 [&zone, &state, &speedDelta, &singleDelta, &netDelta](
273 auto const& entry)
Matthew Barth24623522017-06-21 14:09:57 -0500274 {
Matthew Barthbc651602017-08-10 16:59:43 -0500275 try
Matthew Barth24623522017-06-21 14:09:57 -0500276 {
Matthew Barthbc651602017-08-10 16:59:43 -0500277 T value = zone.template getPropertyValue<T>(
278 entry.first,
279 std::get<intfPos>(entry.second),
280 std::get<propPos>(entry.second));
281 // TODO openbmc/phosphor-fan-presence#7 - Support possible
282 // state types for comparison
283 if (value >= state)
284 {
285 // Increase by at least a single delta
286 // to attempt bringing under 'state'
287 auto delta = std::max((value - state), singleDelta);
288 // Increase is the difference times
289 // the given speed delta
290 netDelta = std::max(netDelta, delta * speedDelta);
291 }
292 }
293 catch (const std::out_of_range& oore)
294 {
295 // Property value not found, netDelta unchanged
Matthew Barth24623522017-06-21 14:09:57 -0500296 }
297 }
298 );
Matthew Barth240397b2017-06-22 11:23:30 -0500299 // Request speed change for target speed update
300 zone.requestSpeedIncrease(netDelta);
Matthew Barth24623522017-06-21 14:09:57 -0500301 };
302}
303
Matthew Barth0ce99d82017-06-22 15:07:29 -0500304/**
305 * @brief An action to set the speed decrease delta and request speed change
306 * @details Provides the ability to determine what the net decrease delta each
307 * zone's fan speeds should be updated by from their current target speed, and
308 * request that speed change occur on the next decrease interval.
309 *
310 * @param[in] state - State to compare the group's property value to
311 * @param[in] speedDelta - Speed delta of the group
312 *
313 * @return Lambda function
314 * A lambda function that determines the net decrease delta and requests
315 * a new target speed with that decrease for the zone.
316 */
317template <typename T>
318auto set_net_decrease_speed(T&& state, uint64_t speedDelta)
319{
320 return [speedDelta,
321 state = std::forward<T>(state)](auto& zone, auto& group)
322 {
323 auto netDelta = zone.getDecSpeedDelta();
324 std::for_each(
325 group.begin(),
326 group.end(),
327 [&zone, &state, &speedDelta, &netDelta](auto const& entry)
328 {
Matthew Barthbc651602017-08-10 16:59:43 -0500329 try
Matthew Barth0ce99d82017-06-22 15:07:29 -0500330 {
Matthew Barthbc651602017-08-10 16:59:43 -0500331 T value = zone.template getPropertyValue<T>(
332 entry.first,
333 std::get<intfPos>(entry.second),
334 std::get<propPos>(entry.second));
335 // TODO openbmc/phosphor-fan-presence#7 - Support possible
336 // state types for comparison
337 if (value < state)
Matthew Barth0ce99d82017-06-22 15:07:29 -0500338 {
Matthew Barthbc651602017-08-10 16:59:43 -0500339 if (netDelta == 0)
340 {
341 netDelta = (state - value) * speedDelta;
342 }
343 else
344 {
345 // Decrease is the difference times
346 // the given speed delta
347 netDelta = std::min(netDelta,
348 (state - value) * speedDelta);
349 }
Matthew Barth0ce99d82017-06-22 15:07:29 -0500350 }
Matthew Barthbc651602017-08-10 16:59:43 -0500351 }
352 catch (const std::out_of_range& oore)
353 {
354 // Property value not found, netDelta unchanged
Matthew Barth0ce99d82017-06-22 15:07:29 -0500355 }
356 }
357 );
358 // Request speed decrease to occur on decrease interval
359 zone.requestSpeedDecrease(netDelta);
360 };
361}
362
Matthew Barth17d1fe22017-05-11 15:00:36 -0500363} // namespace action
364} // namespace control
365} // namespace fan
366} // namespace phosphor