1 #include "actions.hpp"
2 #include "utility.hpp"
3 
4 namespace phosphor
5 {
6 namespace fan
7 {
8 namespace control
9 {
10 namespace action
11 {
12 
13 using namespace phosphor::fan;
14 
15 Action call_actions_based_on_timer(TimerConf&& tConf,
16                                    std::vector<Action>&& actions)
17 {
18     return [tConf = std::move(tConf),
19             actions = std::move(actions)](control::Zone& zone,
20                                           const Group& group)
21     {
22         try
23         {
24             auto it = zone.getTimerEvents().find(__func__);
25             if (it != zone.getTimerEvents().end())
26             {
27                 auto& timers = it->second;
28                 auto timerIter = zone.findTimer(group, actions, timers);
29                 if (timerIter == timers.end())
30                 {
31                     // No timer exists yet for action, add timer
32                     zone.addTimer(__func__, group, actions, tConf);
33                 }
34                 else if (timerIter != timers.end())
35                 {
36                     // Remove any timer for this group
37                     timers.erase(timerIter);
38                     if (timers.empty())
39                     {
40                         zone.getTimerEvents().erase(it);
41                     }
42                 }
43             }
44             else
45             {
46                 // No timer exists yet for event, add timer
47                 zone.addTimer(__func__, group, actions, tConf);
48             }
49         }
50         catch (const std::out_of_range& oore)
51         {
52             // Group not found, no timers set
53         }
54     };
55 }
56 
57 void default_floor_on_missing_owner(Zone& zone, const Group& group)
58 {
59     // Set/update the services of the group
60     zone.setServices(&group);
61     auto services = zone.getGroupServices(&group);
62     auto defFloor = std::any_of(
63         services.begin(),
64         services.end(),
65         [](const auto& s)
66         {
67             return !std::get<hasOwnerPos>(s);
68         });
69     if (defFloor)
70     {
71         zone.setFloor(zone.getDefFloor());
72     }
73     // Update fan control floor change allowed
74     zone.setFloorChangeAllow(&group, !defFloor);
75 }
76 
77 Action set_speed_on_missing_owner(uint64_t speed)
78 {
79     return [speed](control::Zone& zone, const Group& group)
80     {
81         // Set/update the services of the group
82         zone.setServices(&group);
83         auto services = zone.getGroupServices(&group);
84         auto missingOwner = std::any_of(
85             services.begin(),
86             services.end(),
87             [](const auto& s)
88             {
89                 return !std::get<hasOwnerPos>(s);
90             });
91         if (missingOwner)
92         {
93             zone.setSpeed(speed);
94         }
95         // Update group's fan control active allowed based on action results
96         zone.setActiveAllow(&group, !missingOwner);
97     };
98 }
99 
100 void set_request_speed_base_with_max(control::Zone& zone,
101                                      const Group& group)
102 {
103     int64_t base = 0;
104     std::for_each(
105             group.begin(),
106             group.end(),
107             [&zone, &base](auto const& entry)
108         {
109             try
110             {
111                 auto value = zone.template getPropertyValue<int64_t>(
112                         std::get<pathPos>(entry),
113                         std::get<intfPos>(entry),
114                         std::get<propPos>(entry));
115                 base = std::max(base, value);
116             }
117             catch (const std::out_of_range& oore)
118             {
119                 // Property value not found, base request speed unchanged
120             }
121         });
122     // A request speed base of 0 defaults to the current target speed
123     zone.setRequestSpeedBase(base);
124 }
125 
126 Action set_floor_from_average_sensor_value(
127         std::map<int64_t, uint64_t>&& val_to_speed)
128 {
129     return [val_to_speed = std::move(val_to_speed)](control::Zone& zone,
130                                                     const Group& group)
131     {
132         auto speed = zone.getDefFloor();
133         if (group.size() != 0)
134         {
135             auto count = 0;
136             auto sumValue = std::accumulate(
137                     group.begin(),
138                     group.end(),
139                     0,
140                     [&zone, &count](int64_t sum, auto const& entry)
141                     {
142                         try
143                         {
144                             return sum +
145                                 zone.template getPropertyValue<int64_t>(
146                                     std::get<pathPos>(entry),
147                                     std::get<intfPos>(entry),
148                                     std::get<propPos>(entry));
149                         }
150                         catch (const std::out_of_range& oore)
151                         {
152                             count++;
153                             return sum;
154                         }
155                     });
156             if ((group.size() - count) > 0)
157             {
158                 auto groupSize = static_cast<int64_t>(group.size());
159                 auto avgValue = sumValue / (groupSize - count);
160                 auto it = std::find_if(
161                     val_to_speed.begin(),
162                     val_to_speed.end(),
163                     [&avgValue](auto const& entry)
164                     {
165                         return avgValue < entry.first;
166                     }
167                 );
168                 if (it != std::end(val_to_speed))
169                 {
170                     speed = (*it).second;
171                 }
172             }
173         }
174         zone.setFloor(speed);
175     };
176 }
177 
178 Action set_ceiling_from_average_sensor_value(
179         std::map<int64_t, uint64_t>&& val_to_speed)
180 {
181     return [val_to_speed = std::move(val_to_speed)](Zone& zone,
182                                                     const Group& group)
183     {
184         auto speed = zone.getCeiling();
185         if (group.size() != 0)
186         {
187             auto count = 0;
188             auto sumValue = std::accumulate(
189                     group.begin(),
190                     group.end(),
191                     0,
192                     [&zone, &count](int64_t sum, auto const& entry)
193                     {
194                         try
195                         {
196                             return sum +
197                                 zone.template getPropertyValue<int64_t>(
198                                     std::get<pathPos>(entry),
199                                     std::get<intfPos>(entry),
200                                     std::get<propPos>(entry));
201                         }
202                         catch (const std::out_of_range& oore)
203                         {
204                             count++;
205                             return sum;
206                         }
207                     });
208             if ((group.size() - count) > 0)
209             {
210                 auto groupSize = static_cast<int64_t>(group.size());
211                 auto avgValue = sumValue / (groupSize - count);
212                 auto prevValue = zone.swapCeilingKeyValue(avgValue);
213                 if (avgValue != prevValue)
214                 {// Only check if previous and new values differ
215                     if (avgValue < prevValue)
216                     {// Value is decreasing from previous
217                         for (auto it = val_to_speed.rbegin();
218                              it != val_to_speed.rend();
219                              ++it)
220                         {
221                             if (it == val_to_speed.rbegin() &&
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                             else if (std::next(it, 1) == val_to_speed.rend() &&
230                                      avgValue <= it->first)
231                             {
232                                 // Value is at/below first map key, set
233                                 // ceiling speed to the first map key's value
234                                 speed = it->second;
235                                 break;
236                             }
237                             if (avgValue < it->first &&
238                                 it->first <= prevValue)
239                             {
240                                 // Value decreased & transitioned across
241                                 // a map key, update ceiling speed to this
242                                 // map key's value when new value is below
243                                 // map's key and the key is at/below the
244                                 // previous value
245                                 speed = it->second;
246                             }
247                         }
248                     }
249                     else
250                     {// Value is increasing from previous
251                         for (auto it = val_to_speed.begin();
252                              it != val_to_speed.end();
253                              ++it)
254                         {
255                             if (it == val_to_speed.begin() &&
256                                 avgValue <= it->first)
257                             {
258                                 // Value is at/below first map key, set
259                                 // ceiling speed to the first map key's value
260                                 speed = it->second;
261                                 break;
262                             }
263                             else if (std::next(it, 1) == val_to_speed.end() &&
264                                      avgValue >= it->first)
265                             {
266                                 // Value is at/above last map key, set
267                                 // ceiling speed to the last map key's value
268                                 speed = it->second;
269                                 break;
270                             }
271                             if (avgValue > it->first &&
272                                 it->first >= prevValue)
273                             {
274                                 // Value increased & transitioned across
275                                 // a map key, update ceiling speed to this
276                                 // map key's value when new value is above
277                                 // map's key and the key is at/above the
278                                 // previous value
279                                 speed = it->second;
280                             }
281                         }
282                     }
283                 }
284             }
285         }
286         zone.setCeiling(speed);
287     };
288 }
289 
290 Action set_floor_from_median_sensor_value(
291         int64_t lowerBound,
292         int64_t upperBound,
293         std::map<int64_t, uint64_t>&& valueToSpeed)
294 {
295     return [lowerBound,
296             upperBound,
297             valueToSpeed = std::move(valueToSpeed)](control::Zone& zone,
298                                                     const Group& group)
299     {
300         auto speed = zone.getDefFloor();
301         if (group.size() != 0)
302         {
303             std::vector<int64_t> validValues;
304             for (auto const& member : group)
305             {
306                 try
307                 {
308                     auto value = zone.template getPropertyValue<int64_t>(
309                             std::get<pathPos>(member),
310                             std::get<intfPos>(member),
311                             std::get<propPos>(member));
312                     if (value == std::clamp(value, lowerBound, upperBound))
313                     {
314                         // Sensor value is valid
315                         validValues.emplace_back(value);
316                     }
317                 }
318                 catch (const std::out_of_range& oore)
319                 {
320                     continue;
321                 }
322             }
323 
324             if (!validValues.empty())
325             {
326                 auto median = validValues.front();
327                 // Get the determined median value
328                 if (validValues.size() == 2)
329                 {
330                     // For 2 values, use the highest instead of the average
331                     // for a thermally safe floor
332                     median = *std::max_element(validValues.begin(),
333                                                validValues.end());
334                 }
335                 else if (validValues.size() > 2)
336                 {
337                     median = utility::getMedian(validValues);
338                 }
339 
340                 // Use determined median sensor value to find floor speed
341                 auto it = std::find_if(
342                     valueToSpeed.begin(),
343                     valueToSpeed.end(),
344                     [&median](auto const& entry)
345                     {
346                         return median < entry.first;
347                     }
348                 );
349                 if (it != std::end(valueToSpeed))
350                 {
351                     speed = (*it).second;
352                 }
353             }
354         }
355         zone.setFloor(speed);
356     };
357 }
358 
359 } // namespace action
360 } // namespace control
361 } // namespace fan
362 } // namespace phosphor
363