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