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