1 #pragma once
2 
3 #include <algorithm>
4 #include <numeric>
5 
6 namespace phosphor
7 {
8 namespace fan
9 {
10 namespace control
11 {
12 namespace action
13 {
14 
15 /**
16  * @brief An action to set the speed on a zone
17  * @details The zone is held at the given speed when a defined number of
18  * 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  */
28 template <typename T>
29 auto count_state_before_speed(size_t count, T&& state, uint64_t speed)
30 {
31     return [count,
32             speed,
33             state = std::forward<T>(state)](auto& zone, auto& group)
34     {
35         size_t numAtState = std::count_if(
36             group.begin(),
37             group.end(),
38             [&zone, &state](auto const& entry)
39             {
40                 return zone.template getPropertyValue<T>(
41                         entry.first,
42                         std::get<intfPos>(entry.second),
43                         std::get<propPos>(entry.second)) == state;
44             });
45         // Update group's fan control active allowed based on action results
46         zone.setActiveAllow(&group, !(numAtState >= count));
47         if (numAtState >= count)
48         {
49             zone.setSpeed(speed);
50         }
51     };
52 }
53 
54 /**
55  * @brief An action to set the floor speed on a zone
56  * @details Based on the average of the defined sensor group values, the floor
57  * speed is selected from the first map key entry that the average sensor value
58  * is less than.
59  *
60  * @param[in] val_to_speed - Ordered map of sensor value-to-speed
61  *
62  * @return Lambda function
63  *     A lambda function to set the zone's floor speed when the average of
64  *     property values within the group is below the lowest sensor value given
65  */
66 auto set_floor_from_average_sensor_value(
67         std::map<int64_t, uint64_t>&& val_to_speed)
68 {
69     return [val_to_speed = std::move(val_to_speed)](auto& zone, auto& group)
70     {
71         auto speed = zone.getDefFloor();
72         if (group.size() != 0)
73         {
74             auto sumValue = std::accumulate(
75                     group.begin(),
76                     group.end(),
77                     0,
78                     [&zone](int64_t sum, auto const& entry)
79                     {
80                         return sum + zone.template getPropertyValue<int64_t>(
81                                 entry.first,
82                                 std::get<intfPos>(entry.second),
83                                 std::get<propPos>(entry.second));
84                     });
85             auto avgValue= sumValue / group.size();
86             auto it = std::find_if(
87                 val_to_speed.begin(),
88                 val_to_speed.end(),
89                 [&avgValue](auto const& entry)
90                 {
91                     return avgValue < entry.first;
92                 }
93             );
94             if (it != std::end(val_to_speed))
95             {
96                 speed = (*it).second;
97             }
98         }
99         zone.setFloor(speed);
100     };
101 }
102 
103 /**
104  * @brief An action to set the ceiling speed on a zone
105  * @details Based on the average of the defined sensor group values, the ceiling
106  * speed is selected from the map key transition point that the average sensor
107  * value falls within depending on the key values direction from what was
108  * previously read.
109  *
110  * @param[in] val_to_speed - Ordered map of sensor value-to-speed transitions
111  *
112  * @return Lambda function
113  *     A lambda function to set the zone's ceiling speed when the average of
114  *     property values within the group is above(increasing) or
115  *     below(decreasing) the key transition point
116  */
117 auto set_ceiling_from_average_sensor_value(
118         std::map<int64_t, uint64_t>&& val_to_speed)
119 {
120     return [val_to_speed = std::move(val_to_speed)](auto& zone, auto& group)
121     {
122         auto speed = zone.getCeiling();
123         if (group.size() != 0)
124         {
125             auto sumValue = std::accumulate(
126                     group.begin(),
127                     group.end(),
128                     0,
129                     [&zone](int64_t sum, auto const& entry)
130                     {
131                         return sum + zone.template getPropertyValue<int64_t>(
132                                 entry.first,
133                                 std::get<intfPos>(entry.second),
134                                 std::get<propPos>(entry.second));
135                     });
136             auto avgValue = sumValue / group.size();
137             auto prevValue = zone.swapCeilingKeyValue(avgValue);
138             if (avgValue != prevValue)
139             {// Only check if previous and new values differ
140                 if (avgValue < prevValue)
141                 {// Value is decreasing from previous
142                     for (auto it = val_to_speed.rbegin();
143                          it != val_to_speed.rend();
144                          ++it)
145                     {
146                         if (it == val_to_speed.rbegin() &&
147                             avgValue >= it->first)
148                         {
149                             // Value is at/above last map key,
150                             // set ceiling speed to the last map key's value
151                             speed = it->second;
152                             break;
153                         }
154                         else if (std::next(it, 1) == val_to_speed.rend() &&
155                                  avgValue <= it->first)
156                         {
157                             // Value is at/below first map key,
158                             // set ceiling speed to the first map key's value
159                             speed = it->second;
160                             break;
161                         }
162                         if (avgValue < it->first &&
163                             it->first <= prevValue)
164                         {
165                             // Value decreased & transitioned across a map key,
166                             // update ceiling speed to this map key's value
167                             // when new value is below map's key and the key
168                             // is at/below the previous value
169                             speed = it->second;
170                         }
171                     }
172                 }
173                 else
174                 {// Value is increasing from previous
175                     for (auto it = val_to_speed.begin();
176                          it != val_to_speed.end();
177                          ++it)
178                     {
179                         if (it == val_to_speed.begin() &&
180                             avgValue <= it->first)
181                         {
182                             // Value is at/below first map key,
183                             // set ceiling speed to the first map key's value
184                             speed = it->second;
185                             break;
186                         }
187                         else if (std::next(it, 1) == val_to_speed.end() &&
188                                  avgValue >= it->first)
189                         {
190                             // Value is at/above last map key,
191                             // set ceiling speed to the last map key's value
192                             speed = it->second;
193                             break;
194                         }
195                         if (avgValue > it->first &&
196                             it->first >= prevValue)
197                         {
198                             // Value increased & transitioned across a map key,
199                             // update ceiling speed to this map key's value
200                             // when new value is above map's key and the key
201                             // is at/above the previous value
202                             speed = it->second;
203                         }
204                     }
205                 }
206             }
207         }
208         zone.setCeiling(speed);
209     };
210 }
211 
212 /**
213  * @brief An action to set the speed increase delta and request speed change
214  * @details Provides the ability to determine what the net increase delta the
215  * zone's fan speeds should be updated by from their current target speed and
216  * request that new target speed.
217  *
218  * @param[in] state - State to compare the group's property value to
219  * @param[in] speedDelta - Speed delta of the group
220  *
221  * @return Lambda function
222  *     A lambda function that determines the net increase delta and requests
223  * a new target speed with that increase for the zone.
224  */
225 template <typename T>
226 auto set_net_increase_speed(T&& state, uint64_t speedDelta)
227 {
228     return [speedDelta,
229             state = std::forward<T>(state)](auto& zone, auto& group)
230     {
231         auto netDelta = zone.getIncSpeedDelta();
232         std::for_each(
233             group.begin(),
234             group.end(),
235             [&zone, &state, &speedDelta, &netDelta](auto const& entry)
236             {
237                 T value = zone.template getPropertyValue<T>(
238                         entry.first,
239                         std::get<intfPos>(entry.second),
240                         std::get<propPos>(entry.second));
241                 // TODO openbmc/phosphor-fan-presence#7 - Support possible
242                 // state types for comparison
243                 if (value >= state)
244                 {
245                     // Increase by at least a single delta
246                     // to attempt bringing under 'state'
247                     auto delta = std::max((value - state), 1);
248                     // Increase is the difference times the given speed delta
249                     netDelta = std::max(netDelta, delta * speedDelta);
250                 }
251             }
252         );
253         // Request speed change for target speed update
254         zone.requestSpeedIncrease(netDelta);
255     };
256 }
257 
258 /**
259  * @brief An action to set the speed decrease delta and request speed change
260  * @details Provides the ability to determine what the net decrease delta each
261  * zone's fan speeds should be updated by from their current target speed, and
262  * request that speed change occur on the next decrease interval.
263  *
264  * @param[in] state - State to compare the group's property value to
265  * @param[in] speedDelta - Speed delta of the group
266  *
267  * @return Lambda function
268  *     A lambda function that determines the net decrease delta and requests
269  * a new target speed with that decrease for the zone.
270  */
271 template <typename T>
272 auto set_net_decrease_speed(T&& state, uint64_t speedDelta)
273 {
274     return [speedDelta,
275             state = std::forward<T>(state)](auto& zone, auto& group)
276     {
277         auto netDelta = zone.getDecSpeedDelta();
278         std::for_each(
279             group.begin(),
280             group.end(),
281             [&zone, &state, &speedDelta, &netDelta](auto const& entry)
282             {
283                 T value = zone.template getPropertyValue<T>(
284                         entry.first,
285                         std::get<intfPos>(entry.second),
286                         std::get<propPos>(entry.second));
287                 // TODO openbmc/phosphor-fan-presence#7 - Support possible
288                 // state types for comparison
289                 if (value < state)
290                 {
291                     if (netDelta == 0)
292                     {
293                         netDelta = (state - value) * speedDelta;
294                     }
295                     else
296                     {
297                         // Decrease is the difference times
298                         // the given speed delta
299                         netDelta = std::min(netDelta,
300                                             (state - value) * speedDelta);
301                     }
302                 }
303             }
304         );
305         // Request speed decrease to occur on decrease interval
306         zone.requestSpeedDecrease(netDelta);
307     };
308 }
309 
310 } // namespace action
311 } // namespace control
312 } // namespace fan
313 } // namespace phosphor
314