1 #pragma once 2 3 #include <algorithm> 4 #include <numeric> 5 6 namespace phosphor 7 { 8 namespace fan 9 { 10 namespace control 11 { 12 namespace action 13 { 14 15 /** 16 * @brief An action to set the speed on a zone 17 * @details The zone is held at the given speed when a defined number of 18 * properties in the group are set to the given state 19 * 20 * @param[in] count - Number of properties 21 * @param[in] state - Value the property(s) needed to be set at 22 * @param[in] speed - Speed to set the zone to 23 * 24 * @return Lambda function 25 * A lambda function to set the zone speed when the number of properties 26 * within the group are at a certain value 27 */ 28 template <typename T> 29 auto count_state_before_speed(size_t count, T&& state, uint64_t speed) 30 { 31 return [count, 32 speed, 33 state = std::forward<T>(state)](auto& zone, auto& group) 34 { 35 size_t numAtState = std::count_if( 36 group.begin(), 37 group.end(), 38 [&zone, &state](auto const& entry) 39 { 40 try 41 { 42 return zone.template getPropertyValue<T>( 43 entry.first, 44 std::get<intfPos>(entry.second), 45 std::get<propPos>(entry.second)) == state; 46 } 47 catch (const std::out_of_range& oore) 48 { 49 // Default to property not equal when not found 50 return false; 51 } 52 }); 53 if (numAtState >= count) 54 { 55 zone.setSpeed(speed); 56 } 57 // Update group's fan control active allowed based on action results 58 zone.setActiveAllow(&group, !(numAtState >= count)); 59 }; 60 } 61 62 /** 63 * @brief An action to set the floor speed on a zone 64 * @details Based on the average of the defined sensor group values, the floor 65 * speed is selected from the first map key entry that the average sensor value 66 * is less than. 67 * 68 * @param[in] val_to_speed - Ordered map of sensor value-to-speed 69 * 70 * @return Lambda function 71 * A lambda function to set the zone's floor speed when the average of 72 * property values within the group is below the lowest sensor value given 73 */ 74 auto set_floor_from_average_sensor_value( 75 std::map<int64_t, uint64_t>&& val_to_speed) 76 { 77 return [val_to_speed = std::move(val_to_speed)](auto& zone, auto& group) 78 { 79 auto speed = zone.getDefFloor(); 80 if (group.size() != 0) 81 { 82 auto count = 0; 83 auto sumValue = std::accumulate( 84 group.begin(), 85 group.end(), 86 0, 87 [&zone, &count](int64_t sum, auto const& entry) 88 { 89 try 90 { 91 return sum + 92 zone.template getPropertyValue<int64_t>( 93 entry.first, 94 std::get<intfPos>(entry.second), 95 std::get<propPos>(entry.second)); 96 } 97 catch (const std::out_of_range& oore) 98 { 99 count++; 100 return sum; 101 } 102 }); 103 if ((group.size() - count) > 0) 104 { 105 auto avgValue = sumValue / (group.size() - count); 106 auto it = std::find_if( 107 val_to_speed.begin(), 108 val_to_speed.end(), 109 [&avgValue](auto const& entry) 110 { 111 return avgValue < entry.first; 112 } 113 ); 114 if (it != std::end(val_to_speed)) 115 { 116 speed = (*it).second; 117 } 118 } 119 } 120 zone.setFloor(speed); 121 }; 122 } 123 124 /** 125 * @brief An action to set the ceiling speed on a zone 126 * @details Based on the average of the defined sensor group values, the ceiling 127 * speed is selected from the map key transition point that the average sensor 128 * value falls within depending on the key values direction from what was 129 * previously read. 130 * 131 * @param[in] val_to_speed - Ordered map of sensor value-to-speed transitions 132 * 133 * @return Lambda function 134 * A lambda function to set the zone's ceiling speed when the average of 135 * property values within the group is above(increasing) or 136 * below(decreasing) the key transition point 137 */ 138 auto set_ceiling_from_average_sensor_value( 139 std::map<int64_t, uint64_t>&& val_to_speed) 140 { 141 return [val_to_speed = std::move(val_to_speed)](auto& zone, auto& group) 142 { 143 auto speed = zone.getCeiling(); 144 if (group.size() != 0) 145 { 146 auto count = 0; 147 auto sumValue = std::accumulate( 148 group.begin(), 149 group.end(), 150 0, 151 [&zone, &count](int64_t sum, auto const& entry) 152 { 153 try 154 { 155 return sum + 156 zone.template getPropertyValue<int64_t>( 157 entry.first, 158 std::get<intfPos>(entry.second), 159 std::get<propPos>(entry.second)); 160 } 161 catch (const std::out_of_range& oore) 162 { 163 count++; 164 return sum; 165 } 166 }); 167 if ((group.size() - count) > 0) 168 { 169 auto avgValue = sumValue / (group.size() - count); 170 auto prevValue = zone.swapCeilingKeyValue(avgValue); 171 if (avgValue != prevValue) 172 {// Only check if previous and new values differ 173 if (avgValue < prevValue) 174 {// Value is decreasing from previous 175 for (auto it = val_to_speed.rbegin(); 176 it != val_to_speed.rend(); 177 ++it) 178 { 179 if (it == val_to_speed.rbegin() && 180 avgValue >= it->first) 181 { 182 // Value is at/above last map key, set 183 // ceiling speed to the last map key's value 184 speed = it->second; 185 break; 186 } 187 else if (std::next(it, 1) == val_to_speed.rend() && 188 avgValue <= it->first) 189 { 190 // Value is at/below first map key, set 191 // ceiling speed to the first map key's value 192 speed = it->second; 193 break; 194 } 195 if (avgValue < it->first && 196 it->first <= prevValue) 197 { 198 // Value decreased & transitioned across 199 // a map key, update ceiling speed to this 200 // map key's value when new value is below 201 // map's key and the key is at/below the 202 // previous value 203 speed = it->second; 204 } 205 } 206 } 207 else 208 {// Value is increasing from previous 209 for (auto it = val_to_speed.begin(); 210 it != val_to_speed.end(); 211 ++it) 212 { 213 if (it == val_to_speed.begin() && 214 avgValue <= it->first) 215 { 216 // Value is at/below first map key, set 217 // ceiling speed to the first map key's value 218 speed = it->second; 219 break; 220 } 221 else if (std::next(it, 1) == val_to_speed.end() && 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 if (avgValue > it->first && 230 it->first >= prevValue) 231 { 232 // Value increased & transitioned across 233 // a map key, update ceiling speed to this 234 // map key's value when new value is above 235 // map's key and the key is at/above the 236 // previous value 237 speed = it->second; 238 } 239 } 240 } 241 } 242 } 243 } 244 zone.setCeiling(speed); 245 }; 246 } 247 248 /** 249 * @brief An action to set the speed increase delta and request speed change 250 * @details Provides the ability to determine what the net increase delta the 251 * zone's fan speeds should be updated by from their current target speed and 252 * request that new target speed. 253 * 254 * @param[in] state - State to compare the group's property value to 255 * @param[in] factor - Factor to apply to the calculated net delta 256 * @param[in] speedDelta - Speed delta of the group 257 * 258 * @return Lambda function 259 * A lambda function that determines the net increase delta and requests 260 * a new target speed with that increase for the zone. 261 */ 262 template <typename T> 263 auto set_net_increase_speed(T&& state, T&& factor, uint64_t speedDelta) 264 { 265 return [speedDelta, 266 factor = std::forward<T>(factor), 267 state = std::forward<T>(state)](auto& zone, auto& group) 268 { 269 auto netDelta = zone.getIncSpeedDelta(); 270 std::for_each( 271 group.begin(), 272 group.end(), 273 [&zone, &state, &factor, &speedDelta, &netDelta]( 274 auto const& entry) 275 { 276 try 277 { 278 T value = zone.template getPropertyValue<T>( 279 entry.first, 280 std::get<intfPos>(entry.second), 281 std::get<propPos>(entry.second)); 282 // TODO openbmc/phosphor-fan-presence#7 - Support possible 283 // state types for comparison 284 if (value >= state) 285 { 286 // Increase by at least a single delta(factor) 287 // to attempt bringing under 'state' 288 auto delta = std::max( 289 (value - state), 290 factor); 291 // Increase is the factor applied to the 292 // difference times the given speed delta 293 netDelta = std::max( 294 netDelta, 295 (delta/factor) * speedDelta); 296 } 297 } 298 catch (const std::out_of_range& oore) 299 { 300 // Property value not found, netDelta unchanged 301 } 302 } 303 ); 304 // Request speed change for target speed update 305 zone.requestSpeedIncrease(netDelta); 306 }; 307 } 308 309 /** 310 * @brief An action to set the speed decrease delta and request speed change 311 * @details Provides the ability to determine what the net decrease delta each 312 * zone's fan speeds should be updated by from their current target speed, and 313 * request that speed change occur on the next decrease interval. 314 * 315 * @param[in] state - State to compare the group's property value to 316 * @param[in] factor - Factor to apply to the calculated net delta 317 * @param[in] speedDelta - Speed delta of the group 318 * 319 * @return Lambda function 320 * A lambda function that determines the net decrease delta and requests 321 * a new target speed with that decrease for the zone. 322 */ 323 template <typename T> 324 auto set_net_decrease_speed(T&& state, T&& factor, uint64_t speedDelta) 325 { 326 return [speedDelta, 327 factor = std::forward<T>(factor), 328 state = std::forward<T>(state)](auto& zone, auto& group) 329 { 330 auto netDelta = zone.getDecSpeedDelta(); 331 std::for_each( 332 group.begin(), 333 group.end(), 334 [&zone, &state, &factor, &speedDelta, &netDelta](auto const& entry) 335 { 336 try 337 { 338 T value = zone.template getPropertyValue<T>( 339 entry.first, 340 std::get<intfPos>(entry.second), 341 std::get<propPos>(entry.second)); 342 // TODO openbmc/phosphor-fan-presence#7 - Support possible 343 // state types for comparison 344 if (value < state) 345 { 346 if (netDelta == 0) 347 { 348 netDelta = ((state - value)/factor) * speedDelta; 349 } 350 else 351 { 352 // Decrease is the factor applied to the 353 // difference times the given speed delta 354 netDelta = std::min( 355 netDelta, 356 ((state - value)/factor) * speedDelta); 357 } 358 } 359 } 360 catch (const std::out_of_range& oore) 361 { 362 // Property value not found, netDelta unchanged 363 } 364 } 365 ); 366 // Request speed decrease to occur on decrease interval 367 zone.requestSpeedDecrease(netDelta); 368 }; 369 } 370 371 } // namespace action 372 } // namespace control 373 } // namespace fan 374 } // namespace phosphor 375