1 #pragma once
2 
3 #include <algorithm>
4 #include <numeric>
5 #include "types.hpp"
6 #include "zone.hpp"
7 
8 namespace phosphor
9 {
10 namespace fan
11 {
12 namespace control
13 {
14 namespace action
15 {
16 
17 /**
18  * @brief An action that wraps a list of actions with a timer
19  * @details Sets up a list of actions to be invoked when the defined timer
20  * expires (or for each expiration of a repeating timer).
21  *
22  * @param[in] tConf - Timer configuration parameters
23  * @param[in] action - List of actions to be called when the timer expires
24  *
25  * @return Action lambda function
26  *     An Action function that creates a timer
27  */
28 Action call_actions_based_on_timer(
29         TimerConf&& tConf,
30         std::vector<Action>&& actions);
31 
32 /**
33  * @brief An action that sets the floor to the default fan floor speed
34  * @details Sets the fan floor to the defined default fan floor speed when a
35  * service associated to the given group has terminated. Once all services
36  * are functional and providing the sensors again, the fan floor is allowed
37  * to be set normally.
38  *
39  * @param[in] zone - Zone containing fans
40  * @param[in] group - Group of sensors to determine services' states
41  */
42 void default_floor_on_missing_owner(Zone& zone, const Group& group);
43 
44 /**
45  * @brief An action to set a speed when a service owner is missing
46  * @details Sets the fans to the given speed when any service owner associated
47  * to the group is missing. Once all services are functional and providing
48  * the event data again, active fan speed changes are allowed.
49  *
50  * @param[in] speed - Speed to set the zone to
51  *
52  * @return Action lambda function
53  *     An Action function that sets the zone to the given speed if any service
54  *     owners are missing.
55  */
56 Action set_speed_on_missing_owner(uint64_t speed);
57 
58 /**
59  * @brief An action to set the request speed base
60  * @details A new target speed is determined using a speed delta being added
61  * or subtracted, for increases or decrease respectively, from a base speed.
62  * This base speed defaults to be the current target speed or is set to a
63  * different base speed(i.e. the fans' tach feedback speed) to request a new
64  * target from.
65  *
66  * @param[in] zone - Zone containing fans
67  * @param[in] group - Group of sensors to determine base from
68  */
69 void set_request_speed_base_with_max(Zone& zone, const Group& group);
70 
71 /**
72  * @brief An action to set the speed on a zone
73  * @details The zone is held at the given speed when a defined number of
74  * properties in the group are set to the given state
75  *
76  * @param[in] count - Number of properties
77  * @param[in] state - Value the property(s) needed to be set at
78  * @param[in] speed - Speed to set the zone to
79  *
80  * @return Lambda function
81  *     A lambda function to set the zone speed when the number of properties
82  *     within the group are at a certain value
83  */
84 template <typename T>
85 auto count_state_before_speed(size_t count, T&& state, uint64_t speed)
86 {
87     return [count,
88             speed,
89             state = std::forward<T>(state)](auto& zone, auto& group)
90     {
91         size_t numAtState = 0;
92         for (auto& entry : group)
93         {
94             try
95             {
96                 if (zone.template getPropertyValue<T>(
97                         std::get<pathPos>(entry),
98                         std::get<intfPos>(entry),
99                         std::get<propPos>(entry)) == state)
100                 {
101                     numAtState++;
102                 }
103             }
104             catch (const std::out_of_range& oore)
105             {
106                 // Default to property not equal when not found
107             }
108             if (numAtState >= count)
109             {
110                 zone.setSpeed(speed);
111                 break;
112             }
113         }
114         // Update group's fan control active allowed based on action results
115         zone.setActiveAllow(&group, !(numAtState >= count));
116     };
117 }
118 
119 /**
120  * @brief An action to set the floor speed on a zone
121  * @details Based on the average of the defined sensor group values, the floor
122  * speed is selected from the first map key entry that the average sensor value
123  * is less than.
124  *
125  * @param[in] val_to_speed - Ordered map of sensor value-to-speed
126  *
127  * @return Action lambda function
128  *     An Action function to set the zone's floor speed when the average of
129  *     property values within the group is below the lowest sensor value given
130  */
131 Action set_floor_from_average_sensor_value(
132         std::map<int64_t, uint64_t>&& val_to_speed);
133 
134 /**
135  * @brief An action to set the ceiling speed on a zone
136  * @details Based on the average of the defined sensor group values, the
137  * ceiling speed is selected from the map key transition point that the average
138  * sensor value falls within depending on the key values direction from what
139  * was previously read.
140  *
141  * @param[in] val_to_speed - Ordered map of sensor value-to-speed transitions
142  *
143  * @return Action lambda function
144  *     An Action function to set the zone's ceiling speed when the average of
145  *     property values within the group is above(increasing) or
146  *     below(decreasing) the key transition point
147  */
148 Action set_ceiling_from_average_sensor_value(
149         std::map<int64_t, uint64_t>&& val_to_speed);
150 
151 /**
152  * @brief An action to set the speed increase delta and request speed change
153  * @details Provides the ability to determine what the net increase delta the
154  * zone's fan speeds should be updated by from their current target speed and
155  * request that new target speed.
156  *
157  * @param[in] state - State to compare the group's property value to
158  * @param[in] factor - Factor to apply to the calculated net delta
159  * @param[in] speedDelta - Speed delta of the group
160  *
161  * @return Lambda function
162  *     A lambda function that determines the net increase delta and requests
163  * a new target speed with that increase for the zone.
164  */
165 template <typename T>
166 auto set_net_increase_speed(T&& state, T&& factor, uint64_t speedDelta)
167 {
168     return [speedDelta,
169             factor = std::forward<T>(factor),
170             state = std::forward<T>(state)](auto& zone, auto& group)
171     {
172         auto netDelta = zone.getIncSpeedDelta();
173         std::for_each(
174             group.begin(),
175             group.end(),
176             [&zone, &state, &factor, &speedDelta, &netDelta](
177                 auto const& entry)
178             {
179                 try
180                 {
181                     T value = zone.template getPropertyValue<T>(
182                             std::get<pathPos>(entry),
183                             std::get<intfPos>(entry),
184                             std::get<propPos>(entry));
185                     // TODO openbmc/phosphor-fan-presence#7 - Support possible
186                     // state types for comparison
187                     if (value >= state)
188                     {
189                         // Increase by at least a single delta(factor)
190                         // to attempt bringing under 'state'
191                         auto delta = std::max(
192                             (value - state),
193                             factor);
194                         // Increase is the factor applied to the
195                         // difference times the given speed delta
196                         netDelta = std::max(
197                             netDelta,
198                             (delta/factor) * speedDelta);
199                     }
200                 }
201                 catch (const std::out_of_range& oore)
202                 {
203                     // Property value not found, netDelta unchanged
204                 }
205             }
206         );
207         // Request speed change for target speed update
208         zone.requestSpeedIncrease(netDelta);
209     };
210 }
211 
212 /**
213  * @brief An action to set the speed decrease delta and request speed change
214  * @details Provides the ability to determine what the net decrease delta each
215  * zone's fan speeds should be updated by from their current target speed, and
216  * request that speed change occur on the next decrease interval.
217  *
218  * @param[in] state - State to compare the group's property value to
219  * @param[in] factor - Factor to apply to the calculated net delta
220  * @param[in] speedDelta - Speed delta of the group
221  *
222  * @return Lambda function
223  *     A lambda function that determines the net decrease delta and requests
224  * a new target speed with that decrease for the zone.
225  */
226 template <typename T>
227 auto set_net_decrease_speed(T&& state, T&& factor, uint64_t speedDelta)
228 {
229     return [speedDelta,
230             factor = std::forward<T>(factor),
231             state = std::forward<T>(state)](auto& zone, auto& group)
232     {
233         auto netDelta = zone.getDecSpeedDelta();
234         for (auto& entry : group)
235         {
236             try
237             {
238                 T value = zone.template getPropertyValue<T>(
239                         std::get<pathPos>(entry),
240                         std::get<intfPos>(entry),
241                         std::get<propPos>(entry));
242                 // TODO openbmc/phosphor-fan-presence#7 - Support possible
243                 // state types for comparison
244                 if (value < state)
245                 {
246                     if (netDelta == 0)
247                     {
248                         netDelta = ((state - value)/factor) * speedDelta;
249                     }
250                     else
251                     {
252                         // Decrease is the factor applied to the
253                         // difference times the given speed delta
254                         netDelta = std::min(
255                             netDelta,
256                             ((state - value)/factor) * speedDelta);
257                     }
258                 }
259                 else
260                 {
261                     // No decrease allowed for this group
262                     netDelta = 0;
263                     break;
264                 }
265             }
266             catch (const std::out_of_range& oore)
267             {
268                 // Property value not found, netDelta unchanged
269             }
270         }
271         // Update group's decrease allowed state
272         zone.setDecreaseAllow(&group, !(netDelta == 0));
273         // Request speed decrease to occur on decrease interval
274         zone.requestSpeedDecrease(netDelta);
275     };
276 }
277 
278 /**
279  * @brief An action to use an alternate set of events
280  * @details Provides the ability to replace a default set of events with an
281  * alternate set of events based on all members of a group being at a specified
282  * state. When any member of the group no longer matches the provided state,
283  * the alternate set of events are replaced with the defaults.
284  *
285  * @param[in] state - State to compare the group's property value to
286  * @param[in] defEvents - The default set of events
287  * @param[in] altEvents - The alternate set of events
288  *
289  * @return Lambda function
290  *     A lambda function that checks all group members are at a specified state
291  * and replacing the default set of events with an alternate set of events.
292  */
293 template <typename T>
294 auto use_alternate_events_on_state(T&& state,
295                                    std::vector<SetSpeedEvent>&& defEvents,
296                                    std::vector<SetSpeedEvent>&& altEvents)
297 {
298     return [state = std::forward<T>(state),
299             defEvents = std::move(defEvents),
300             altEvents = std::move(altEvents)](auto& zone, auto& group)
301     {
302         // Compare all group entries to the state
303         auto useAlt = std::all_of(
304             group.begin(),
305             group.end(),
306             [&zone, &state](auto const& entry)
307             {
308                 try
309                 {
310                     return zone.template getPropertyValue<T>(
311                             std::get<pathPos>(entry),
312                             std::get<intfPos>(entry),
313                             std::get<propPos>(entry)) == state;
314                 }
315                 catch (const std::out_of_range& oore)
316                 {
317                     // Default to property not equal when not found
318                     return false;
319                 }
320             });
321 
322         const std::vector<SetSpeedEvent> *rmEvents = &altEvents;
323         const std::vector<SetSpeedEvent> *initEvents = &defEvents;
324 
325         if (useAlt)
326         {
327             rmEvents = &defEvents;
328             initEvents = &altEvents;
329         }
330 
331         // Remove events
332         std::for_each(
333             rmEvents->begin(),
334             rmEvents->end(),
335             [&zone](auto const& entry)
336             {
337                 zone.removeEvent(entry);
338             });
339         // Init events
340         std::for_each(
341             initEvents->begin(),
342             initEvents->end(),
343             [&zone](auto const& entry)
344             {
345                 zone.initEvent(entry);
346             });
347     };
348 }
349 
350 /**
351  * @brief An action to set the floor speed on a zone
352  * @details Using sensor group values that are within a defined range, the
353  * floor speed is selected from the first map key entry that the median
354  * sensor value is less than where 3 or more sensor group values are valid.
355  * In the case where less than 3 sensor values are valid, use the highest
356  * sensor group value and default the floor speed when 0 sensor group values
357  * are valid.
358  *
359  * @param[in] lowerBound - Lowest allowed sensor value to be valid
360  * @param[in] upperBound - Highest allowed sensor value to be valid
361  * @param[in] valueToSpeed - Ordered map of sensor value-to-speed
362  *
363  * @return Action lambda function
364  *     An Action function to set the zone's floor speed from a resulting group
365  * of valid sensor values based on their highest value or median.
366  */
367 Action set_floor_from_median_sensor_value(
368         int64_t lowerBound,
369         int64_t upperBound,
370         std::map<int64_t, uint64_t>&& valueToSpeed);
371 
372 /**
373  * @brief An action to update the default floor speed
374  * @details Provides the ability to update the default fan floor speed when
375  * all of the group members property values match the value given
376  *
377  * @param[in] state - State to compare the group's property value to
378  * @param[in] speed - Speed to set the default fan floor to
379  *
380  * @return Lambda function
381  *     A lambda function that checks all group members are at a specified state
382  * and updates the default fan floor speed.
383  */
384 template <typename T>
385 auto update_default_floor(T&& state, uint64_t speed)
386 {
387     return [speed, state = std::forward<T>(state)](auto& zone, auto& group)
388     {
389         auto updateDefFloor = std::all_of(
390             group.begin(),
391             group.end(),
392             [&zone, &state](auto const& entry)
393             {
394                 try
395                 {
396                     return zone.template getPropertyValue<T>(
397                             std::get<pathPos>(entry),
398                             std::get<intfPos>(entry),
399                             std::get<propPos>(entry)) == state;
400                 }
401                 catch (const std::out_of_range& oore)
402                 {
403                     // Default to property not equal when not found
404                     return false;
405                 }
406             });
407 
408         if (!updateDefFloor)
409         {
410             // Do not update the default floor
411             return;
412         }
413 
414         // Set/update the default floor of the zone
415         zone.setDefFloor(speed);
416     };
417 }
418 
419 /**
420  * @brief An action to use a set of events
421  * @details Provides the ability to use a set of events when all members of
422  * a group are at a specified state. When any member of the group no longer
423  * matches the provided state the set of events are removed.
424  *
425  * @param[in] state - State to compare the group's property value to
426  * @param[in] events - The set of events
427  *
428  * @return Lambda function
429  *     A lambda function that checks all group members are at a specified state
430  * and initializes the set of events, otherwise removes them.
431  */
432 template <typename T>
433 auto use_events_on_state(T&& state,
434                          std::vector<SetSpeedEvent>&& events)
435 {
436     return [state = std::forward<T>(state),
437             events = std::move(events)](auto& zone, auto& group)
438     {
439         // Compare all group entries to the state
440         auto useEvents = std::all_of(
441             group.begin(),
442             group.end(),
443             [&zone, &state](auto const& entry)
444             {
445                 try
446                 {
447                     return zone.template getPropertyValue<T>(
448                             std::get<pathPos>(entry),
449                             std::get<intfPos>(entry),
450                             std::get<propPos>(entry)) == state;
451                 }
452                 catch (const std::out_of_range& oore)
453                 {
454                     // Default to property not equal when not found
455                     return false;
456                 }
457             });
458 
459         if (useEvents)
460         {
461             // Init events
462             std::for_each(
463                 events.begin(),
464                 events.end(),
465                 [&zone](auto const& entry)
466                 {
467                     zone.initEvent(entry);
468                 });
469         }
470         else
471         {
472             // Remove events
473             std::for_each(
474                 events.begin(),
475                 events.end(),
476                 [&zone](auto const& entry)
477                 {
478                     zone.removeEvent(entry);
479                 });
480         }
481     };
482 }
483 
484 } // namespace action
485 } // namespace control
486 } // namespace fan
487 } // namespace phosphor
488