1 #include "actions.hpp" 2 #include "utility.hpp" 3 4 namespace phosphor 5 { 6 namespace fan 7 { 8 namespace control 9 { 10 namespace action 11 { 12 13 using namespace phosphor::fan; 14 15 Action call_actions_based_on_timer(TimerConf&& tConf, 16 std::vector<Action>&& actions) 17 { 18 return [tConf = std::move(tConf), 19 actions = std::move(actions)](control::Zone& zone, 20 const Group& group) 21 { 22 try 23 { 24 auto it = zone.getTimerEvents().find(__func__); 25 if (it != zone.getTimerEvents().end()) 26 { 27 auto& timers = it->second; 28 auto timerIter = zone.findTimer(group, actions, timers); 29 if (timerIter == timers.end()) 30 { 31 // No timer exists yet for action, add timer 32 zone.addTimer(__func__, group, actions, tConf); 33 } 34 else if (timerIter != timers.end()) 35 { 36 // Remove any timer for this group 37 timers.erase(timerIter); 38 if (timers.empty()) 39 { 40 zone.getTimerEvents().erase(it); 41 } 42 } 43 } 44 else 45 { 46 // No timer exists yet for event, add timer 47 zone.addTimer(__func__, group, actions, tConf); 48 } 49 } 50 catch (const std::out_of_range& oore) 51 { 52 // Group not found, no timers set 53 } 54 }; 55 } 56 57 void default_floor_on_missing_owner(Zone& zone, const Group& group) 58 { 59 // Set/update the services of the group 60 zone.setServices(&group); 61 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 77 Action set_speed_on_missing_owner(uint64_t speed) 78 { 79 return [speed](control::Zone& zone, const Group& group) 80 { 81 // Set/update the services of the group 82 zone.setServices(&group); 83 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 100 void 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>( 112 std::get<pathPos>(entry), 113 std::get<intfPos>(entry), 114 std::get<propPos>(entry)); 115 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 126 Action 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>( 146 std::get<pathPos>(entry), 147 std::get<intfPos>(entry), 148 std::get<propPos>(entry)); 149 } 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 178 Action 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>( 198 std::get<pathPos>(entry), 199 std::get<intfPos>(entry), 200 std::get<propPos>(entry)); 201 } 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 290 Action 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 359 } // namespace action 360 } // namespace control 361 } // namespace fan 362 } // namespace phosphor 363