xref: /openbmc/phosphor-fan-presence/presence/tach.cpp (revision 9d533806250cea56406bdd39e025f0d820c4ed90)
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 "tach.hpp"
17 
18 #include "logging.hpp"
19 #include "rpolicy.hpp"
20 
21 #include <phosphor-logging/lg2.hpp>
22 
23 #include <format>
24 #include <string>
25 #include <tuple>
26 #include <vector>
27 
28 namespace phosphor
29 {
30 namespace fan
31 {
32 namespace presence
33 {
34 
35 using namespace std::literals::string_literals;
36 
37 static const auto tachNamespace = "/xyz/openbmc_project/sensors/fan_tach/"s;
38 static const auto tachIface = "xyz.openbmc_project.Sensor.Value"s;
39 static const auto tachProperty = "Value"s;
40 
Tach(const std::vector<std::string> & sensors)41 Tach::Tach(const std::vector<std::string>& sensors) : currentState(false)
42 {
43     // Initialize state.
44     for (const auto& s : sensors)
45     {
46         state.emplace_back(s, nullptr, 0);
47     }
48 }
49 
start()50 bool Tach::start()
51 {
52     for (size_t i = 0; i < state.size(); ++i)
53     {
54         auto& s = state[i];
55         auto tachPath = tachNamespace + std::get<std::string>(s);
56 
57         // Register for signal callbacks.
58         std::get<1>(s) = std::make_unique<sdbusplus::bus::match_t>(
59             util::SDBusPlus::getBus(),
60             sdbusplus::bus::match::rules::propertiesChanged(tachPath,
61                                                             tachIface),
62             [this, i](auto& msg) { this->propertiesChanged(i, msg); });
63 
64         // Get an initial tach speed.
65         try
66         {
67             std::get<double>(s) = util::SDBusPlus::getProperty<double>(
68                 tachPath, tachIface, tachProperty);
69         }
70         catch (const std::exception&)
71         {
72             // Assume not spinning.
73 
74             std::get<double>(s) = 0;
75             lg2::info("Unable to read fan tach sensor {TACPATH}", "TACPATH",
76                       tachPath);
77         }
78     }
79 
80     // Set the initial state of the sensor.
81     currentState = std::any_of(state.begin(), state.end(), [](const auto& s) {
82         return std::get<double>(s) != 0;
83     });
84 
85     return currentState;
86 }
87 
stop()88 void Tach::stop()
89 {
90     for (auto& s : state)
91     {
92         // De-register signal callbacks.
93         std::get<1>(s) = nullptr;
94     }
95 }
96 
present()97 bool Tach::present()
98 {
99     // Live query the tach readings.
100     std::vector<double> values;
101     for (const auto& s : state)
102     {
103         values.push_back(util::SDBusPlus::getProperty<double>(
104             tachNamespace + std::get<std::string>(s), tachIface, tachProperty));
105     }
106 
107     return std::any_of(values.begin(), values.end(),
108                        [](const auto& v) { return v != 0; });
109 }
110 
propertiesChanged(size_t sensor,sdbusplus::message_t & msg)111 void Tach::propertiesChanged(size_t sensor, sdbusplus::message_t& msg)
112 {
113     std::string iface;
114     util::Properties<double> properties;
115     msg.read(iface, properties);
116 
117     propertiesChanged(sensor, properties);
118 }
119 
propertiesChanged(size_t sensor,const util::Properties<double> & props)120 void Tach::propertiesChanged(size_t sensor,
121                              const util::Properties<double>& props)
122 {
123     // Find the Value property containing the speed.
124     auto it = props.find(tachProperty);
125     if (it != props.end())
126     {
127         auto& s = state[sensor];
128         std::get<double>(s) = std::get<double>(it->second);
129 
130         auto newState =
131             std::any_of(state.begin(), state.end(),
132                         [](const auto& s) { return std::get<double>(s) != 0; });
133 
134         if (currentState != newState)
135         {
136             getPolicy().stateChanged(newState, *this);
137             currentState = newState;
138         }
139     }
140 }
141 
logConflict(const std::string & fanInventoryPath) const142 void Tach::logConflict(const std::string& fanInventoryPath) const
143 {
144     getLogger().log(std::format(
145         "Tach sensor presence detect for fan {} said not present but "
146         "other methods indicated present",
147         fanInventoryPath));
148 
149     // Let the code that monitors fan faults create the event
150     // logs for stopped rotors.
151 }
152 
153 } // namespace presence
154 } // namespace fan
155 } // namespace phosphor
156