blob: 0388b5c42ddba02d516a5e525126a94e204de76f [file] [log] [blame]
Matthew Barthb280bfa2017-09-15 09:56:50 -05001#include "actions.hpp"
Matthew Barth014f07c2019-05-30 09:55:42 -05002#include "utility.hpp"
Matthew Barthb280bfa2017-09-15 09:56:50 -05003
4namespace phosphor
5{
6namespace fan
7{
8namespace control
9{
10namespace action
11{
12
13using namespace phosphor::fan;
14
William A. Kennington III122b8432018-10-30 18:39:21 -070015Action call_actions_based_on_timer(TimerConf&& tConf,
16 std::vector<Action>&& actions)
Matthew Barth2a646c52017-10-05 17:04:11 -050017{
18 return [tConf = std::move(tConf),
19 actions = std::move(actions)](control::Zone& zone,
20 const Group& group)
21 {
22 try
23 {
Matthew Barthd7b716a2018-11-16 13:37:57 -060024 auto it = zone.getTimerEvents().find(__func__);
25 if (it != zone.getTimerEvents().end())
Matthew Barth2a646c52017-10-05 17:04:11 -050026 {
Matthew Barthd7b716a2018-11-16 13:37:57 -060027 auto& timers = it->second;
28 auto timerIter = zone.findTimer(group, actions, timers);
Matthew Barthe7d53892018-12-12 10:59:57 -060029 if (timerIter == timers.end())
Matthew Barth2a646c52017-10-05 17:04:11 -050030 {
Matthew Barthd7b716a2018-11-16 13:37:57 -060031 // No timer exists yet for action, add timer
32 zone.addTimer(__func__, group, actions, tConf);
Matthew Barth2a646c52017-10-05 17:04:11 -050033 }
Matthew Barthe7d53892018-12-12 10:59:57 -060034 else if (timerIter != timers.end())
Matthew Barthd7b716a2018-11-16 13:37:57 -060035 {
36 // Remove any timer for this group
37 timers.erase(timerIter);
38 if (timers.empty())
39 {
40 zone.getTimerEvents().erase(it);
41 }
42 }
43 }
Matthew Barthe7d53892018-12-12 10:59:57 -060044 else
Matthew Barthd7b716a2018-11-16 13:37:57 -060045 {
46 // No timer exists yet for event, add timer
47 zone.addTimer(__func__, group, actions, tConf);
Matthew Barth2a646c52017-10-05 17:04:11 -050048 }
49 }
50 catch (const std::out_of_range& oore)
51 {
52 // Group not found, no timers set
53 }
54 };
55}
56
Matthew Barth98726c42017-10-17 10:35:20 -050057void default_floor_on_missing_owner(Zone& zone, const Group& group)
58{
Matthew Barth480787c2017-11-06 11:00:00 -060059 // Set/update the services of the group
60 zone.setServices(&group);
Matthew Barth98726c42017-10-17 10:35:20 -050061 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
Matthew Barth0decd1b2017-10-24 15:58:17 -050077Action set_speed_on_missing_owner(uint64_t speed)
78{
79 return [speed](control::Zone& zone, const Group& group)
80 {
Matthew Barth480787c2017-11-06 11:00:00 -060081 // Set/update the services of the group
82 zone.setServices(&group);
Matthew Barth0decd1b2017-10-24 15:58:17 -050083 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
Matthew Barthb280bfa2017-09-15 09:56:50 -0500100void 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>(
Matthew Barth146b7392018-03-08 16:17:58 -0600112 std::get<pathPos>(entry),
113 std::get<intfPos>(entry),
114 std::get<propPos>(entry));
Matthew Barthb280bfa2017-09-15 09:56:50 -0500115 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
126Action 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>(
Matthew Barth146b7392018-03-08 16:17:58 -0600146 std::get<pathPos>(entry),
147 std::get<intfPos>(entry),
148 std::get<propPos>(entry));
Matthew Barthb280bfa2017-09-15 09:56:50 -0500149 }
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
178Action 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>(
Matthew Barth146b7392018-03-08 16:17:58 -0600198 std::get<pathPos>(entry),
199 std::get<intfPos>(entry),
200 std::get<propPos>(entry));
Matthew Barthb280bfa2017-09-15 09:56:50 -0500201 }
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
Matthew Barth014f07c2019-05-30 09:55:42 -0500290Action 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
Matthew Barthb280bfa2017-09-15 09:56:50 -0500359} // namespace action
360} // namespace control
361} // namespace fan
362} // namespace phosphor