xref: /openbmc/phosphor-fan-presence/presence/anyof.cpp (revision dfddd648cb81b27492afead4e2346f5fcd1397cb)
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  
AnyOf(const Fan & fan,const std::vector<std::reference_wrapper<PresenceSensor>> & s,std::unique_ptr<EEPROMDevice> e)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)), state(),
40      _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  
stateChanged(bool present,PresenceSensor & sensor)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  
monitor()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  
checkSensorConflicts()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(state.begin(), state.end(),
143                      [this](const auto& s) {
144                          return std::get<presentPos>(s);
145                      }) &&
146          !std::all_of(state.begin(), state.end(),
147                       [this](const auto& s) { return std::get<presentPos>(s); }))
148      {
149          for (auto& [sensor, present, conflict] : state)
150          {
151              if (!present && !conflict)
152              {
153                  sensor.get().logConflict(invNamespace + std::get<1>(fan));
154                  conflict = true;
155              }
156          }
157      }
158  }
159  
powerStateChanged(bool powerOn)160  void AnyOf::powerStateChanged(bool powerOn)
161  {
162      if (powerOn)
163      {
164          // Clear the conflict state from last time
165          std::for_each(state.begin(), state.end(), [](auto& state) {
166              std::get<conflictPos>(state) = false;
167          });
168  
169          // Wait to give the fans time to start spinning before
170          // considering power actually on.
171          _powerOnDelayTimer.restartOnce(powerOnDelayTime);
172      }
173      else
174      {
175          _powerOn = false;
176  
177          if (_powerOnDelayTimer.isEnabled())
178          {
179              _powerOnDelayTimer.setEnabled(false);
180          }
181      }
182  }
183  
delayedAfterPowerOn()184  void AnyOf::delayedAfterPowerOn()
185  {
186      _powerOn = true;
187      checkSensorConflicts();
188  }
189  
190  } // namespace presence
191  } // namespace fan
192  } // namespace phosphor
193