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