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 return zone.template getPropertyValue<T>( 41 entry.first, 42 std::get<intfPos>(entry.second), 43 std::get<propPos>(entry.second)) == state; 44 }); 45 // Update group's fan control active allowed based on action results 46 zone.setActiveAllow(&group, !(numAtState >= count)); 47 if (numAtState >= count) 48 { 49 zone.setSpeed(speed); 50 } 51 }; 52 } 53 54 /** 55 * @brief An action to set the floor speed on a zone 56 * @details Based on the average of the defined sensor group values, the floor 57 * speed is selected from the first map key entry that the average sensor value 58 * is less than. 59 * 60 * @param[in] val_to_speed - Ordered map of sensor value-to-speed 61 * 62 * @return Lambda function 63 * A lambda function to set the zone's floor speed when the average of 64 * property values within the group is below the lowest sensor value given 65 */ 66 auto set_floor_from_average_sensor_value( 67 std::map<int64_t, uint64_t>&& val_to_speed) 68 { 69 return [val_to_speed = std::move(val_to_speed)](auto& zone, auto& group) 70 { 71 auto speed = zone.getDefFloor(); 72 if (group.size() != 0) 73 { 74 auto sumValue = std::accumulate( 75 group.begin(), 76 group.end(), 77 0, 78 [&zone](int64_t sum, auto const& entry) 79 { 80 return sum + zone.template getPropertyValue<int64_t>( 81 entry.first, 82 std::get<intfPos>(entry.second), 83 std::get<propPos>(entry.second)); 84 }); 85 auto avgValue= sumValue / group.size(); 86 auto it = std::find_if( 87 val_to_speed.begin(), 88 val_to_speed.end(), 89 [&avgValue](auto const& entry) 90 { 91 return avgValue < entry.first; 92 } 93 ); 94 if (it != std::end(val_to_speed)) 95 { 96 speed = (*it).second; 97 } 98 } 99 zone.setFloor(speed); 100 }; 101 } 102 103 /** 104 * @brief An action to set the ceiling speed on a zone 105 * @details Based on the average of the defined sensor group values, the ceiling 106 * speed is selected from the map key transition point that the average sensor 107 * value falls within depending on the key values direction from what was 108 * previously read. 109 * 110 * @param[in] val_to_speed - Ordered map of sensor value-to-speed transitions 111 * 112 * @return Lambda function 113 * A lambda function to set the zone's ceiling speed when the average of 114 * property values within the group is above(increasing) or 115 * below(decreasing) the key transition point 116 */ 117 auto set_ceiling_from_average_sensor_value( 118 std::map<int64_t, uint64_t>&& val_to_speed) 119 { 120 return [val_to_speed = std::move(val_to_speed)](auto& zone, auto& group) 121 { 122 auto speed = zone.getCeiling(); 123 if (group.size() != 0) 124 { 125 auto sumValue = std::accumulate( 126 group.begin(), 127 group.end(), 128 0, 129 [&zone](int64_t sum, auto const& entry) 130 { 131 return sum + zone.template getPropertyValue<int64_t>( 132 entry.first, 133 std::get<intfPos>(entry.second), 134 std::get<propPos>(entry.second)); 135 }); 136 auto avgValue = sumValue / group.size(); 137 auto prevValue = zone.swapCeilingKeyValue(avgValue); 138 if (avgValue != prevValue) 139 {// Only check if previous and new values differ 140 if (avgValue < prevValue) 141 {// Value is decreasing from previous 142 for (auto it = val_to_speed.rbegin(); 143 it != val_to_speed.rend(); 144 ++it) 145 { 146 if (it == val_to_speed.rbegin() && 147 avgValue >= it->first) 148 { 149 // Value is at/above last map key, 150 // set ceiling speed to the last map key's value 151 speed = it->second; 152 break; 153 } 154 else if (std::next(it, 1) == val_to_speed.rend() && 155 avgValue <= it->first) 156 { 157 // Value is at/below first map key, 158 // set ceiling speed to the first map key's value 159 speed = it->second; 160 break; 161 } 162 if (avgValue < it->first && 163 it->first <= prevValue) 164 { 165 // Value decreased & transitioned across a map key, 166 // update ceiling speed to this map key's value 167 // when new value is below map's key and the key 168 // is at/below the previous value 169 speed = it->second; 170 } 171 } 172 } 173 else 174 {// Value is increasing from previous 175 for (auto it = val_to_speed.begin(); 176 it != val_to_speed.end(); 177 ++it) 178 { 179 if (it == val_to_speed.begin() && 180 avgValue <= it->first) 181 { 182 // Value is at/below first map key, 183 // set ceiling speed to the first map key's value 184 speed = it->second; 185 break; 186 } 187 else if (std::next(it, 1) == val_to_speed.end() && 188 avgValue >= it->first) 189 { 190 // Value is at/above last map key, 191 // set ceiling speed to the last map key's value 192 speed = it->second; 193 break; 194 } 195 if (avgValue > it->first && 196 it->first >= prevValue) 197 { 198 // Value increased & transitioned across a map key, 199 // update ceiling speed to this map key's value 200 // when new value is above map's key and the key 201 // is at/above the previous value 202 speed = it->second; 203 } 204 } 205 } 206 } 207 } 208 zone.setCeiling(speed); 209 }; 210 } 211 212 /** 213 * @brief An action to set the speed increase delta and request speed change 214 * @details Provides the ability to determine what the net increase delta the 215 * zone's fan speeds should be updated by from their current target speed and 216 * request that new target speed. 217 * 218 * @param[in] state - State to compare the group's property value to 219 * @param[in] speedDelta - Speed delta of the group 220 * 221 * @return Lambda function 222 * A lambda function that determines the net increase delta and requests 223 * a new target speed with that increase for the zone. 224 */ 225 template <typename T> 226 auto set_net_increase_speed(T&& state, uint64_t speedDelta) 227 { 228 return [speedDelta, 229 state = std::forward<T>(state)](auto& zone, auto& group) 230 { 231 auto netDelta = zone.getIncSpeedDelta(); 232 std::for_each( 233 group.begin(), 234 group.end(), 235 [&zone, &state, &speedDelta, &netDelta](auto const& entry) 236 { 237 T value = zone.template getPropertyValue<T>( 238 entry.first, 239 std::get<intfPos>(entry.second), 240 std::get<propPos>(entry.second)); 241 // TODO openbmc/phosphor-fan-presence#7 - Support possible 242 // state types for comparison 243 if (value >= state) 244 { 245 // Increase by at least a single delta 246 // to attempt bringing under 'state' 247 auto delta = std::max((value - state), 1); 248 // Increase is the difference times the given speed delta 249 netDelta = std::max(netDelta, delta * speedDelta); 250 } 251 } 252 ); 253 // Request speed change for target speed update 254 zone.requestSpeedIncrease(netDelta); 255 }; 256 } 257 258 /** 259 * @brief An action to set the speed decrease delta and request speed change 260 * @details Provides the ability to determine what the net decrease delta each 261 * zone's fan speeds should be updated by from their current target speed, and 262 * request that speed change occur on the next decrease interval. 263 * 264 * @param[in] state - State to compare the group's property value to 265 * @param[in] speedDelta - Speed delta of the group 266 * 267 * @return Lambda function 268 * A lambda function that determines the net decrease delta and requests 269 * a new target speed with that decrease for the zone. 270 */ 271 template <typename T> 272 auto set_net_decrease_speed(T&& state, uint64_t speedDelta) 273 { 274 return [speedDelta, 275 state = std::forward<T>(state)](auto& zone, auto& group) 276 { 277 auto netDelta = zone.getDecSpeedDelta(); 278 std::for_each( 279 group.begin(), 280 group.end(), 281 [&zone, &state, &speedDelta, &netDelta](auto const& entry) 282 { 283 T value = zone.template getPropertyValue<T>( 284 entry.first, 285 std::get<intfPos>(entry.second), 286 std::get<propPos>(entry.second)); 287 // TODO openbmc/phosphor-fan-presence#7 - Support possible 288 // state types for comparison 289 if (value < state) 290 { 291 if (netDelta == 0) 292 { 293 netDelta = (state - value) * speedDelta; 294 } 295 else 296 { 297 // Decrease is the difference times 298 // the given speed delta 299 netDelta = std::min(netDelta, 300 (state - value) * speedDelta); 301 } 302 } 303 } 304 ); 305 // Request speed decrease to occur on decrease interval 306 zone.requestSpeedDecrease(netDelta); 307 }; 308 } 309 310 } // namespace action 311 } // namespace control 312 } // namespace fan 313 } // namespace phosphor 314