1 /** 2 * Copyright © 2017 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "anyof.hpp" 17 18 #include "fan.hpp" 19 #include "get_power_state.hpp" 20 #include "psensor.hpp" 21 22 #include <phosphor-logging/log.hpp> 23 24 #include <algorithm> 25 26 namespace phosphor 27 { 28 namespace fan 29 { 30 namespace presence 31 { 32 33 using namespace std::chrono_literals; 34 static const auto powerOnDelayTime = 5s; 35 36 AnyOf::AnyOf(const Fan& fan, 37 const std::vector<std::reference_wrapper<PresenceSensor>>& s, 38 std::unique_ptr<EEPROMDevice> e) : 39 RedundancyPolicy(fan, std::move(e)), 40 state(), _powerState(getPowerStateObject()), 41 _powerOnDelayTimer(sdeventplus::Event::get_default(), 42 std::bind(&AnyOf::delayedAfterPowerOn, this)), 43 _powerOn(false) 44 { 45 for (auto& sensor : s) 46 { 47 state.emplace_back(sensor, false, false); 48 } 49 50 _powerState->addCallback( 51 std::get<1>(fan) + "-anyOf", 52 std::bind(&AnyOf::powerStateChanged, this, std::placeholders::_1)); 53 54 // If power is already on, give the fans some time to spin up 55 // before considering power to actually be on. 56 if (_powerState->isPowerOn()) 57 { 58 _powerOnDelayTimer.restartOnce(powerOnDelayTime); 59 } 60 } 61 62 void AnyOf::stateChanged(bool present, PresenceSensor& sensor) 63 { 64 // Find the sensor that changed state. 65 auto sit = 66 std::find_if(state.begin(), state.end(), [&sensor](const auto& s) { 67 return std::get<sensorPos>(s).get() == sensor; 68 }); 69 if (sit != state.end()) 70 { 71 auto origState = 72 std::any_of(state.begin(), state.end(), 73 [](const auto& s) { return std::get<presentPos>(s); }); 74 75 // Update our cache of the sensors state and re-evaluate. 76 std::get<presentPos>(*sit) = present; 77 auto newState = 78 std::any_of(state.begin(), state.end(), 79 [](const auto& s) { return std::get<presentPos>(s); }); 80 setPresence(fan, newState); 81 82 if (eepromDevice && (newState != origState)) 83 { 84 if (newState) 85 { 86 eepromDevice->bind(); 87 } 88 else 89 { 90 eepromDevice->unbind(); 91 } 92 } 93 94 // At least 1 sensor said a fan was present, check if any disagree. 95 if (newState) 96 { 97 if (!origState) 98 { 99 // Fan plug detected, re-enable conflict logging 100 std::for_each(state.begin(), state.end(), [](auto& s) { 101 std::get<conflictPos>(s) = false; 102 }); 103 } 104 105 checkSensorConflicts(); 106 } 107 } 108 } 109 110 void AnyOf::monitor() 111 { 112 // Start all sensors in the anyof redundancy set. 113 114 for (auto& s : state) 115 { 116 auto& sensor = std::get<sensorPos>(s); 117 std::get<presentPos>(s) = sensor.get().start(); 118 } 119 120 auto present = std::any_of(state.begin(), state.end(), [](const auto& s) { 121 return std::get<presentPos>(s); 122 }); 123 setPresence(fan, present); 124 125 // At least one of the contained methods indicated present, 126 // so check that they all agree. 127 if (present) 128 { 129 checkSensorConflicts(); 130 } 131 } 132 133 void AnyOf::checkSensorConflicts() 134 { 135 if (!isPowerOn()) 136 { 137 return; 138 } 139 140 // If at least one, but not all, sensors indicate present, then 141 // tell the not present ones to log a conflict if not already done. 142 if (std::any_of( 143 state.begin(), state.end(), 144 [this](const auto& s) { return std::get<presentPos>(s); }) && 145 !std::all_of(state.begin(), state.end(), 146 [this](const auto& s) { return std::get<presentPos>(s); })) 147 { 148 for (auto& [sensor, present, conflict] : state) 149 { 150 if (!present && !conflict) 151 { 152 sensor.get().logConflict(invNamespace + std::get<1>(fan)); 153 conflict = true; 154 } 155 } 156 } 157 } 158 159 void AnyOf::powerStateChanged(bool powerOn) 160 { 161 if (powerOn) 162 { 163 // Clear the conflict state from last time 164 std::for_each(state.begin(), state.end(), [](auto& state) { 165 std::get<conflictPos>(state) = false; 166 }); 167 168 // Wait to give the fans time to start spinning before 169 // considering power actually on. 170 _powerOnDelayTimer.restartOnce(powerOnDelayTime); 171 } 172 else 173 { 174 _powerOn = false; 175 176 if (_powerOnDelayTimer.isEnabled()) 177 { 178 _powerOnDelayTimer.setEnabled(false); 179 } 180 } 181 } 182 183 void AnyOf::delayedAfterPowerOn() 184 { 185 _powerOn = true; 186 checkSensorConflicts(); 187 } 188 189 } // namespace presence 190 } // namespace fan 191 } // namespace phosphor 192