1 #pragma once 2 3 #include <algorithm> 4 #include <numeric> 5 #include "types.hpp" 6 #include "zone.hpp" 7 #include "utility.hpp" 8 9 namespace phosphor 10 { 11 namespace fan 12 { 13 namespace control 14 { 15 namespace action 16 { 17 18 /** 19 * @brief An action that wraps a list of actions with a timer 20 * @details Sets up a list of actions to be invoked when the defined timer 21 * expires (or for each expiration of a repeating timer). 22 * 23 * @param[in] tConf - Timer configuration parameters 24 * @param[in] action - List of actions to be called when the timer expires 25 * 26 * @return Action lambda function 27 * An Action function that creates a timer 28 */ 29 Action call_actions_based_on_timer( 30 TimerConf&& tConf, 31 std::vector<Action>&& actions); 32 33 /** 34 * @brief An action that sets the floor to the default fan floor speed 35 * @details Sets the fan floor to the defined default fan floor speed when a 36 * service associated to the given group has terminated. Once all services 37 * are functional and providing the sensors again, the fan floor is allowed 38 * to be set normally. 39 * 40 * @param[in] zone - Zone containing fans 41 * @param[in] group - Group of sensors to determine services' states 42 */ 43 void default_floor_on_missing_owner(Zone& zone, const Group& group); 44 45 /** 46 * @brief An action to set a speed when a service owner is missing 47 * @details Sets the fans to the given speed when any service owner associated 48 * to the group is missing. Once all services are functional and providing 49 * the event data again, active fan speed changes are allowed. 50 * 51 * @param[in] speed - Speed to set the zone to 52 * 53 * @return Action lambda function 54 * An Action function that sets the zone to the given speed if any service 55 * owners are missing. 56 */ 57 Action set_speed_on_missing_owner(uint64_t speed); 58 59 /** 60 * @brief An action to set the request speed base 61 * @details A new target speed is determined using a speed delta being added 62 * or subtracted, for increases or decrease respectively, from a base speed. 63 * This base speed defaults to be the current target speed or is set to a 64 * different base speed(i.e. the fans' tach feedback speed) to request a new 65 * target from. 66 * 67 * @param[in] zone - Zone containing fans 68 * @param[in] group - Group of sensors to determine base from 69 */ 70 void set_request_speed_base_with_max(Zone& zone, const Group& group); 71 72 /** 73 * @brief An action to set the speed on a zone 74 * @details The zone is held at the given speed when a defined number of 75 * properties in the group are set to the given state 76 * 77 * @param[in] count - Number of properties 78 * @param[in] state - Value the property(s) needed to be set at 79 * @param[in] speed - Speed to set the zone to 80 * 81 * @return Lambda function 82 * A lambda function to set the zone speed when the number of properties 83 * within the group are at a certain value 84 */ 85 template <typename T> 86 auto count_state_before_speed(size_t count, T&& state, uint64_t speed) 87 { 88 return [count, 89 speed, 90 state = std::forward<T>(state)](auto& zone, auto& group) 91 { 92 size_t numAtState = 0; 93 for (auto& entry : group) 94 { 95 try 96 { 97 if (zone.template getPropertyValue<T>( 98 std::get<pathPos>(entry), 99 std::get<intfPos>(entry), 100 std::get<propPos>(entry)) == state) 101 { 102 numAtState++; 103 } 104 } 105 catch (const std::out_of_range& oore) 106 { 107 // Default to property not equal when not found 108 } 109 if (numAtState >= count) 110 { 111 zone.setSpeed(speed); 112 break; 113 } 114 } 115 // Update group's fan control active allowed based on action results 116 zone.setActiveAllow(&group, !(numAtState >= count)); 117 }; 118 } 119 120 /** 121 * @brief An action to set the floor speed on a zone 122 * @details Based on the average of the defined sensor group values, the floor 123 * speed is selected from the first map key entry that the average sensor value 124 * is less than. 125 * 126 * @param[in] val_to_speed - Ordered map of sensor value-to-speed 127 * 128 * @return Action lambda function 129 * An Action function to set the zone's floor speed when the average of 130 * property values within the group is below the lowest sensor value given 131 */ 132 template <typename T> 133 Action set_floor_from_average_sensor_value( 134 std::map<T, uint64_t>&& val_to_speed) 135 { 136 return [val_to_speed = std::move(val_to_speed)](control::Zone& zone, 137 const Group& group) 138 { 139 auto speed = zone.getDefFloor(); 140 if (group.size() != 0) 141 { 142 auto count = 0; 143 auto sumValue = std::accumulate( 144 group.begin(), 145 group.end(), 146 0, 147 [&zone, &count](T sum, auto const& entry) 148 { 149 try 150 { 151 return sum + 152 zone.template getPropertyValue<T>( 153 std::get<pathPos>(entry), 154 std::get<intfPos>(entry), 155 std::get<propPos>(entry)); 156 } 157 catch (const std::out_of_range& oore) 158 { 159 count++; 160 return sum; 161 } 162 }); 163 if ((group.size() - count) > 0) 164 { 165 auto groupSize = static_cast<int64_t>(group.size()); 166 auto avgValue = sumValue / (groupSize - count); 167 auto it = std::find_if( 168 val_to_speed.begin(), 169 val_to_speed.end(), 170 [&avgValue](auto const& entry) 171 { 172 return avgValue < entry.first; 173 } 174 ); 175 if (it != std::end(val_to_speed)) 176 { 177 speed = (*it).second; 178 } 179 } 180 } 181 zone.setFloor(speed); 182 }; 183 } 184 185 /** 186 * @brief An action to set the ceiling speed on a zone 187 * @details Based on the average of the defined sensor group values, the 188 * ceiling speed is selected from the map key transition point that the average 189 * sensor value falls within depending on the key values direction from what 190 * was previously read. 191 * 192 * @param[in] val_to_speed - Ordered map of sensor value-to-speed transitions 193 * 194 * @return Action lambda function 195 * An Action function to set the zone's ceiling speed when the average of 196 * property values within the group is above(increasing) or 197 * below(decreasing) the key transition point 198 */ 199 template <typename T> 200 Action set_ceiling_from_average_sensor_value( 201 std::map<T, uint64_t>&& val_to_speed) 202 { 203 return [val_to_speed = std::move(val_to_speed)](Zone& zone, 204 const Group& group) 205 { 206 auto speed = zone.getCeiling(); 207 if (group.size() != 0) 208 { 209 auto count = 0; 210 auto sumValue = std::accumulate( 211 group.begin(), 212 group.end(), 213 0, 214 [&zone, &count](T sum, auto const& entry) 215 { 216 try 217 { 218 return sum + 219 zone.template getPropertyValue<T>( 220 std::get<pathPos>(entry), 221 std::get<intfPos>(entry), 222 std::get<propPos>(entry)); 223 } 224 catch (const std::out_of_range& oore) 225 { 226 count++; 227 return sum; 228 } 229 }); 230 if ((group.size() - count) > 0) 231 { 232 auto groupSize = static_cast<int64_t>(group.size()); 233 auto avgValue = sumValue / (groupSize - count); 234 auto prevValue = zone.swapCeilingKeyValue(avgValue); 235 if (avgValue != prevValue) 236 {// Only check if previous and new values differ 237 if (avgValue < prevValue) 238 {// Value is decreasing from previous 239 for (auto it = val_to_speed.rbegin(); 240 it != val_to_speed.rend(); 241 ++it) 242 { 243 if (it == val_to_speed.rbegin() && 244 avgValue >= it->first) 245 { 246 // Value is at/above last map key, set 247 // ceiling speed to the last map key's value 248 speed = it->second; 249 break; 250 } 251 else if (std::next(it, 1) == val_to_speed.rend() && 252 avgValue <= it->first) 253 { 254 // Value is at/below first map key, set 255 // ceiling speed to the first map key's value 256 speed = it->second; 257 break; 258 } 259 if (avgValue < it->first && 260 it->first <= prevValue) 261 { 262 // Value decreased & transitioned across 263 // a map key, update ceiling speed to this 264 // map key's value when new value is below 265 // map's key and the key is at/below the 266 // previous value 267 speed = it->second; 268 } 269 } 270 } 271 else 272 {// Value is increasing from previous 273 for (auto it = val_to_speed.begin(); 274 it != val_to_speed.end(); 275 ++it) 276 { 277 if (it == val_to_speed.begin() && 278 avgValue <= it->first) 279 { 280 // Value is at/below first map key, set 281 // ceiling speed to the first map key's value 282 speed = it->second; 283 break; 284 } 285 else if (std::next(it, 1) == val_to_speed.end() && 286 avgValue >= it->first) 287 { 288 // Value is at/above last map key, set 289 // ceiling speed to the last map key's value 290 speed = it->second; 291 break; 292 } 293 if (avgValue > it->first && 294 it->first >= prevValue) 295 { 296 // Value increased & transitioned across 297 // a map key, update ceiling speed to this 298 // map key's value when new value is above 299 // map's key and the key is at/above the 300 // previous value 301 speed = it->second; 302 } 303 } 304 } 305 } 306 } 307 } 308 zone.setCeiling(speed); 309 }; 310 } 311 312 /** 313 * @brief An action to set the speed increase delta and request speed change 314 * @details Provides the ability to determine what the net increase delta the 315 * zone's fan speeds should be updated by from their current target speed and 316 * request that new target speed. 317 * 318 * @param[in] state - State to compare the group's property value to 319 * @param[in] factor - Factor to apply to the calculated net delta 320 * @param[in] speedDelta - Speed delta of the group 321 * 322 * @return Lambda function 323 * A lambda function that determines the net increase delta and requests 324 * a new target speed with that increase for the zone. 325 */ 326 template <typename T> 327 auto set_net_increase_speed(T&& state, T&& factor, uint64_t speedDelta) 328 { 329 return [speedDelta, 330 factor = std::forward<T>(factor), 331 state = std::forward<T>(state)](auto& zone, auto& group) 332 { 333 auto netDelta = zone.getIncSpeedDelta(); 334 std::for_each( 335 group.begin(), 336 group.end(), 337 [&zone, &state, &factor, &speedDelta, &netDelta]( 338 auto const& entry) 339 { 340 try 341 { 342 T value = zone.template getPropertyValue<T>( 343 std::get<pathPos>(entry), 344 std::get<intfPos>(entry), 345 std::get<propPos>(entry)); 346 // TODO openbmc/phosphor-fan-presence#7 - Support possible 347 // state types for comparison 348 if (value >= state) 349 { 350 // Increase by at least a single delta(factor) 351 // to attempt bringing under 'state' 352 auto delta = std::max( 353 (value - state), 354 factor); 355 // Increase is the factor applied to the 356 // difference times the given speed delta 357 netDelta = std::max( 358 netDelta, 359 static_cast<uint64_t>((delta/factor) * speedDelta)); 360 } 361 } 362 catch (const std::out_of_range& oore) 363 { 364 // Property value not found, netDelta unchanged 365 } 366 } 367 ); 368 // Request speed change for target speed update 369 zone.requestSpeedIncrease(netDelta); 370 }; 371 } 372 373 /** 374 * @brief An action to set the speed decrease delta and request speed change 375 * @details Provides the ability to determine what the net decrease delta each 376 * zone's fan speeds should be updated by from their current target speed, and 377 * request that speed change occur on the next decrease interval. 378 * 379 * @param[in] state - State to compare the group's property value to 380 * @param[in] factor - Factor to apply to the calculated net delta 381 * @param[in] speedDelta - Speed delta of the group 382 * 383 * @return Lambda function 384 * A lambda function that determines the net decrease delta and requests 385 * a new target speed with that decrease for the zone. 386 */ 387 template <typename T> 388 auto set_net_decrease_speed(T&& state, T&& factor, uint64_t speedDelta) 389 { 390 return [speedDelta, 391 factor = std::forward<T>(factor), 392 state = std::forward<T>(state)](auto& zone, auto& group) 393 { 394 auto netDelta = zone.getDecSpeedDelta(); 395 for (auto& entry : group) 396 { 397 try 398 { 399 T value = zone.template getPropertyValue<T>( 400 std::get<pathPos>(entry), 401 std::get<intfPos>(entry), 402 std::get<propPos>(entry)); 403 // TODO openbmc/phosphor-fan-presence#7 - Support possible 404 // state types for comparison 405 if (value < state) 406 { 407 if (netDelta == 0) 408 { 409 netDelta = ((state - value)/factor) * speedDelta; 410 } 411 else 412 { 413 // Decrease is the factor applied to the 414 // difference times the given speed delta 415 netDelta = std::min( 416 netDelta, 417 static_cast<uint64_t>( 418 ((state - value)/factor) * speedDelta)); 419 } 420 } 421 else 422 { 423 // No decrease allowed for this group 424 netDelta = 0; 425 break; 426 } 427 } 428 catch (const std::out_of_range& oore) 429 { 430 // Property value not found, netDelta unchanged 431 } 432 } 433 // Update group's decrease allowed state 434 zone.setDecreaseAllow(&group, !(netDelta == 0)); 435 // Request speed decrease to occur on decrease interval 436 zone.requestSpeedDecrease(netDelta); 437 }; 438 } 439 440 /** 441 * @brief An action to use an alternate set of events 442 * @details Provides the ability to replace a default set of events with an 443 * alternate set of events based on all members of a group being at a specified 444 * state. When any member of the group no longer matches the provided state, 445 * the alternate set of events are replaced with the defaults. 446 * 447 * @param[in] state - State to compare the group's property value to 448 * @param[in] defEvents - The default set of events 449 * @param[in] altEvents - The alternate set of events 450 * 451 * @return Lambda function 452 * A lambda function that checks all group members are at a specified state 453 * and replacing the default set of events with an alternate set of events. 454 */ 455 template <typename T> 456 auto use_alternate_events_on_state(T&& state, 457 std::vector<SetSpeedEvent>&& defEvents, 458 std::vector<SetSpeedEvent>&& altEvents) 459 { 460 return [state = std::forward<T>(state), 461 defEvents = std::move(defEvents), 462 altEvents = std::move(altEvents)](auto& zone, auto& group) 463 { 464 // Compare all group entries to the state 465 auto useAlt = std::all_of( 466 group.begin(), 467 group.end(), 468 [&zone, &state](auto const& entry) 469 { 470 try 471 { 472 return zone.template getPropertyValue<T>( 473 std::get<pathPos>(entry), 474 std::get<intfPos>(entry), 475 std::get<propPos>(entry)) == state; 476 } 477 catch (const std::out_of_range& oore) 478 { 479 // Default to property not equal when not found 480 return false; 481 } 482 }); 483 484 const std::vector<SetSpeedEvent> *rmEvents = &altEvents; 485 const std::vector<SetSpeedEvent> *initEvents = &defEvents; 486 487 if (useAlt) 488 { 489 rmEvents = &defEvents; 490 initEvents = &altEvents; 491 } 492 493 // Remove events 494 std::for_each( 495 rmEvents->begin(), 496 rmEvents->end(), 497 [&zone](auto const& entry) 498 { 499 zone.removeEvent(entry); 500 }); 501 // Init events 502 std::for_each( 503 initEvents->begin(), 504 initEvents->end(), 505 [&zone](auto const& entry) 506 { 507 zone.initEvent(entry); 508 }); 509 }; 510 } 511 512 /** 513 * @brief An action to set the floor speed on a zone 514 * @details Using sensor group values that are within a defined range, the 515 * floor speed is selected from the first map key entry that the median 516 * sensor value is less than where 3 or more sensor group values are valid. 517 * In the case where less than 3 sensor values are valid, use the highest 518 * sensor group value and default the floor speed when 0 sensor group values 519 * are valid. 520 * 521 * @param[in] lowerBound - Lowest allowed sensor value to be valid 522 * @param[in] upperBound - Highest allowed sensor value to be valid 523 * @param[in] valueToSpeed - Ordered map of sensor value-to-speed 524 * 525 * @return Action lambda function 526 * An Action function to set the zone's floor speed from a resulting group 527 * of valid sensor values based on their highest value or median. 528 */ 529 template <typename T> 530 Action set_floor_from_median_sensor_value( 531 T&& lowerBound, 532 T&& upperBound, 533 std::map<T, uint64_t>&& valueToSpeed) 534 { 535 return [lowerBound = std::forward<T>(lowerBound), 536 upperBound = std::forward<T>(upperBound), 537 valueToSpeed = std::move(valueToSpeed)](control::Zone& zone, 538 const Group& group) 539 { 540 auto speed = zone.getDefFloor(); 541 if (group.size() != 0) 542 { 543 std::vector<T> validValues; 544 for (auto const& member : group) 545 { 546 try 547 { 548 auto value = zone.template getPropertyValue<T>( 549 std::get<pathPos>(member), 550 std::get<intfPos>(member), 551 std::get<propPos>(member)); 552 if (value == std::clamp(value, lowerBound, upperBound)) 553 { 554 // Sensor value is valid 555 validValues.emplace_back(value); 556 } 557 } 558 catch (const std::out_of_range& oore) 559 { 560 continue; 561 } 562 } 563 564 if (!validValues.empty()) 565 { 566 auto median = validValues.front(); 567 // Get the determined median value 568 if (validValues.size() == 2) 569 { 570 // For 2 values, use the highest instead of the average 571 // for a thermally safe floor 572 median = *std::max_element(validValues.begin(), 573 validValues.end()); 574 } 575 else if (validValues.size() > 2) 576 { 577 median = utility::getMedian(validValues); 578 } 579 580 // Use determined median sensor value to find floor speed 581 auto it = std::find_if( 582 valueToSpeed.begin(), 583 valueToSpeed.end(), 584 [&median](auto const& entry) 585 { 586 return median < entry.first; 587 } 588 ); 589 if (it != std::end(valueToSpeed)) 590 { 591 speed = (*it).second; 592 } 593 } 594 } 595 zone.setFloor(speed); 596 }; 597 } 598 599 /** 600 * @brief An action to update the default floor speed 601 * @details Provides the ability to update the default fan floor speed when 602 * all of the group members property values match the value given 603 * 604 * @param[in] state - State to compare the group's property value to 605 * @param[in] speed - Speed to set the default fan floor to 606 * 607 * @return Lambda function 608 * A lambda function that checks all group members are at a specified state 609 * and updates the default fan floor speed. 610 */ 611 template <typename T> 612 auto update_default_floor(T&& state, uint64_t speed) 613 { 614 return [speed, state = std::forward<T>(state)](auto& zone, auto& group) 615 { 616 auto updateDefFloor = std::all_of( 617 group.begin(), 618 group.end(), 619 [&zone, &state](auto const& entry) 620 { 621 try 622 { 623 return zone.template getPropertyValue<T>( 624 std::get<pathPos>(entry), 625 std::get<intfPos>(entry), 626 std::get<propPos>(entry)) == state; 627 } 628 catch (const std::out_of_range& oore) 629 { 630 // Default to property not equal when not found 631 return false; 632 } 633 }); 634 635 if (!updateDefFloor) 636 { 637 // Do not update the default floor 638 return; 639 } 640 641 // Set/update the default floor of the zone 642 zone.setDefFloor(speed); 643 }; 644 } 645 646 /** 647 * @brief An action to use a set of events 648 * @details Provides the ability to use a set of events when all members of 649 * a group are at a specified state. When any member of the group no longer 650 * matches the provided state the set of events are removed. 651 * 652 * @param[in] state - State to compare the group's property value to 653 * @param[in] events - The set of events 654 * 655 * @return Lambda function 656 * A lambda function that checks all group members are at a specified state 657 * and initializes the set of events, otherwise removes them. 658 */ 659 template <typename T> 660 auto use_events_on_state(T&& state, 661 std::vector<SetSpeedEvent>&& events) 662 { 663 return [state = std::forward<T>(state), 664 events = std::move(events)](auto& zone, auto& group) 665 { 666 // Compare all group entries to the state 667 auto useEvents = std::all_of( 668 group.begin(), 669 group.end(), 670 [&zone, &state](auto const& entry) 671 { 672 try 673 { 674 return zone.template getPropertyValue<T>( 675 std::get<pathPos>(entry), 676 std::get<intfPos>(entry), 677 std::get<propPos>(entry)) == state; 678 } 679 catch (const std::out_of_range& oore) 680 { 681 // Default to property not equal when not found 682 return false; 683 } 684 }); 685 686 if (useEvents) 687 { 688 // Init events 689 std::for_each( 690 events.begin(), 691 events.end(), 692 [&zone](auto const& entry) 693 { 694 zone.initEvent(entry); 695 }); 696 } 697 else 698 { 699 // Remove events 700 std::for_each( 701 events.begin(), 702 events.end(), 703 [&zone](auto const& entry) 704 { 705 zone.removeEvent(entry); 706 }); 707 } 708 }; 709 } 710 711 } // namespace action 712 } // namespace control 713 } // namespace fan 714 } // namespace phosphor 715