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     RedundancyPolicy(fan),
39     state(), _powerState(getPowerStateObject()),
40     _powerOnDelayTimer(sdeventplus::Event::get_default(),
41                        std::bind(&AnyOf::delayedAfterPowerOn, this)),
42     _powerOn(false)
43 {
44     for (auto& sensor : s)
45     {
46         state.emplace_back(sensor, false, false);
47     }
48 
49     _powerState->addCallback(
50         std::get<1>(fan) + "-anyOf",
51         std::bind(&AnyOf::powerStateChanged, this, std::placeholders::_1));
52 
53     // If power is already on, give the fans some time to spin up
54     // before considering power to actually be on.
55     if (_powerState->isPowerOn())
56     {
57         _powerOnDelayTimer.restartOnce(powerOnDelayTime);
58     }
59 }
60 
61 void AnyOf::stateChanged(bool present, PresenceSensor& sensor)
62 {
63     // Find the sensor that changed state.
64     auto sit =
65         std::find_if(state.begin(), state.end(), [&sensor](const auto& s) {
66             return std::get<sensorPos>(s).get() == sensor;
67         });
68     if (sit != state.end())
69     {
70         auto origState =
71             std::any_of(state.begin(), state.end(),
72                         [](const auto& s) { return std::get<presentPos>(s); });
73 
74         // Update our cache of the sensors state and re-evaluate.
75         std::get<presentPos>(*sit) = present;
76         auto newState =
77             std::any_of(state.begin(), state.end(),
78                         [](const auto& s) { return std::get<presentPos>(s); });
79         setPresence(fan, newState);
80 
81         // At least 1 sensor said a fan was present, check if any disagree.
82         if (newState)
83         {
84             if (!origState)
85             {
86                 // Fan plug detected, re-enable conflict logging
87                 std::for_each(state.begin(), state.end(), [](auto& s) {
88                     std::get<conflictPos>(s) = false;
89                 });
90             }
91 
92             checkSensorConflicts();
93         }
94     }
95 }
96 
97 void AnyOf::monitor()
98 {
99     // Start all sensors in the anyof redundancy set.
100 
101     for (auto& s : state)
102     {
103         auto& sensor = std::get<sensorPos>(s);
104         std::get<presentPos>(s) = sensor.get().start();
105     }
106 
107     auto present = std::any_of(state.begin(), state.end(), [](const auto& s) {
108         return std::get<presentPos>(s);
109     });
110     setPresence(fan, present);
111 
112     // At least one of the contained methods indicated present,
113     // so check that they all agree.
114     if (present)
115     {
116         checkSensorConflicts();
117     }
118 }
119 
120 void AnyOf::checkSensorConflicts()
121 {
122     if (!isPowerOn())
123     {
124         return;
125     }
126 
127     // If at least one, but not all, sensors indicate present, then
128     // tell the not present ones to log a conflict if not already done.
129     if (std::any_of(
130             state.begin(), state.end(),
131             [this](const auto& s) { return std::get<presentPos>(s); }) &&
132         !std::all_of(state.begin(), state.end(),
133                      [this](const auto& s) { return std::get<presentPos>(s); }))
134     {
135         for (auto& [sensor, present, conflict] : state)
136         {
137             if (!present && !conflict)
138             {
139                 sensor.get().logConflict(invNamespace + std::get<1>(fan));
140                 conflict = true;
141             }
142         }
143     }
144 }
145 
146 void AnyOf::powerStateChanged(bool powerOn)
147 {
148     if (powerOn)
149     {
150         // Clear the conflict state from last time
151         std::for_each(state.begin(), state.end(), [](auto& state) {
152             std::get<conflictPos>(state) = false;
153         });
154 
155         // Wait to give the fans time to start spinning before
156         // considering power actually on.
157         _powerOnDelayTimer.restartOnce(powerOnDelayTime);
158     }
159     else
160     {
161         _powerOn = false;
162 
163         if (_powerOnDelayTimer.isEnabled())
164         {
165             _powerOnDelayTimer.setEnabled(false);
166         }
167     }
168 }
169 
170 void AnyOf::delayedAfterPowerOn()
171 {
172     _powerOn = true;
173     checkSensorConflicts();
174 }
175 
176 } // namespace presence
177 } // namespace fan
178 } // namespace phosphor
179