xref: /openbmc/dbus-sensors/src/Thresholds.cpp (revision 251c7823)
1 #include <Thresholds.hpp>
2 #include <VariantVisitors.hpp>
3 #include <boost/algorithm/string/replace.hpp>
4 #include <boost/lexical_cast.hpp>
5 #include <fstream>
6 #include <iostream>
7 #include <sensor.hpp>
8 
9 static constexpr bool DEBUG = false;
10 constexpr size_t MAX_THRESHOLDS = 4;
11 
12 namespace thresholds
13 {
14 unsigned int toBusValue(const Level &level)
15 {
16     switch (level)
17     {
18         case (Level::WARNING):
19         {
20             return 0;
21         }
22         case (Level::CRITICAL):
23         {
24             return 1;
25         }
26         default:
27         {
28             return -1;
29         }
30     }
31 }
32 
33 std::string toBusValue(const Direction &direction)
34 {
35     switch (direction)
36     {
37         case (Direction::LOW):
38         {
39             return "less than";
40         }
41         case (Direction::HIGH):
42         {
43             return "greater than";
44         }
45         default:
46         {
47             return "err";
48         }
49     }
50 }
51 
52 bool ParseThresholdsFromConfig(
53     const SensorData &sensorData,
54     std::vector<thresholds::Threshold> &thresholdVector,
55     const std::string *matchLabel)
56 {
57     for (const auto &item : sensorData)
58     {
59         if (item.first.find("Thresholds") == std::string::npos)
60         {
61             continue;
62         }
63         if (matchLabel != nullptr)
64         {
65             auto labelFind = item.second.find("Label");
66             if (labelFind == item.second.end())
67                 continue;
68             if (mapbox::util::apply_visitor(VariantToStringVisitor(),
69                                             labelFind->second) != *matchLabel)
70                 continue;
71         }
72         auto directionFind = item.second.find("Direction");
73         auto severityFind = item.second.find("Severity");
74         auto valueFind = item.second.find("Value");
75         if (valueFind == item.second.end() ||
76             severityFind == item.second.end() ||
77             directionFind == item.second.end())
78         {
79             std::cerr << "Malformed threshold in configuration\n";
80             return false;
81         }
82         Level level;
83         Direction direction;
84         if (mapbox::util::apply_visitor(VariantToUnsignedIntVisitor(),
85                                         severityFind->second) == 0)
86         {
87             level = Level::WARNING;
88         }
89         else
90         {
91             level = Level::CRITICAL;
92         }
93         if (mapbox::util::apply_visitor(VariantToStringVisitor(),
94                                         directionFind->second) == "less than")
95         {
96             direction = Direction::LOW;
97         }
98         else
99         {
100             direction = Direction::HIGH;
101         }
102         float val = mapbox::util::apply_visitor(VariantToFloatVisitor(),
103                                                 valueFind->second);
104 
105         thresholdVector.emplace_back(level, direction, val);
106     }
107     return true;
108 }
109 
110 void persistThreshold(const std::string &path, const std::string &baseInterface,
111                       const thresholds::Threshold &threshold,
112                       std::shared_ptr<sdbusplus::asio::connection> &conn)
113 {
114     for (int ii = 0; ii < MAX_THRESHOLDS; ii++)
115     {
116         std::string thresholdInterface =
117             baseInterface + ".Thresholds" + std::to_string(ii);
118         conn->async_method_call(
119             [&, path, threshold, thresholdInterface](
120                 const boost::system::error_code &ec,
121                 const boost::container::flat_map<std::string, BasicVariantType>
122                     &result) {
123                 if (ec)
124                 {
125                     return; // threshold not supported
126                 }
127 
128                 auto directionFind = result.find("Direction");
129                 auto severityFind = result.find("Severity");
130                 auto valueFind = result.find("Value");
131                 if (valueFind == result.end() || severityFind == result.end() ||
132                     directionFind == result.end())
133                 {
134                     std::cerr << "Malformed threshold in configuration\n";
135                     return;
136                 }
137                 unsigned int level = mapbox::util::apply_visitor(
138                     VariantToUnsignedIntVisitor(), severityFind->second);
139 
140                 std::string dir = mapbox::util::apply_visitor(
141                     VariantToStringVisitor(), directionFind->second);
142                 if ((toBusValue(threshold.level) != level) ||
143                     (toBusValue(threshold.direction) != dir))
144                 {
145                     return; // not the droid we're looking for
146                 }
147 
148                 sdbusplus::message::variant<double> value(threshold.value);
149                 conn->async_method_call(
150                     [](const boost::system::error_code &ec) {
151                         if (ec)
152                         {
153                             std::cerr << "Error setting threshold " << ec
154                                       << "\n";
155                         }
156                     },
157                     ENTITY_MANAGER_NAME, path,
158                     "org.freedesktop.DBus.Properties", "Set",
159                     thresholdInterface, "Value", value);
160             },
161             ENTITY_MANAGER_NAME, path, "org.freedesktop.DBus.Properties",
162             "GetAll", thresholdInterface);
163     }
164 }
165 
166 void checkThresholds(Sensor *sensor)
167 {
168 
169     if (sensor->thresholds.empty())
170     {
171         return;
172     }
173     for (auto &threshold : sensor->thresholds)
174     {
175         if (threshold.direction == thresholds::Direction::HIGH)
176         {
177             if (sensor->value > threshold.value && !threshold.asserted)
178             {
179                 assertThresholds(sensor, threshold.level, threshold.direction,
180                                  true);
181                 threshold.asserted = true;
182             }
183             else if (sensor->value <= threshold.value && threshold.asserted)
184             {
185                 assertThresholds(sensor, threshold.level, threshold.direction,
186                                  false);
187                 threshold.asserted = false;
188             }
189         }
190         else
191         {
192             if (sensor->value < threshold.value && !threshold.asserted)
193             {
194                 assertThresholds(sensor, threshold.level, threshold.direction,
195                                  true);
196                 threshold.asserted = true;
197             }
198             else if (sensor->value >= threshold.value && threshold.asserted)
199             {
200                 assertThresholds(sensor, threshold.level, threshold.direction,
201                                  false);
202                 threshold.asserted = false;
203             }
204         }
205     }
206 }
207 
208 void assertThresholds(Sensor *sensor, thresholds::Level level,
209                       thresholds::Direction direction, bool assert)
210 {
211     std::string property;
212     std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
213     if (level == thresholds::Level::WARNING &&
214         direction == thresholds::Direction::HIGH)
215     {
216         property = "WarningAlarmHigh";
217         interface = sensor->thresholdInterfaceWarning;
218     }
219     else if (level == thresholds::Level::WARNING &&
220              direction == thresholds::Direction::LOW)
221     {
222         property = "WarningAlarmLow";
223         interface = sensor->thresholdInterfaceWarning;
224     }
225     else if (level == thresholds::Level::CRITICAL &&
226              direction == thresholds::Direction::HIGH)
227     {
228         property = "CriticalAlarmHigh";
229         interface = sensor->thresholdInterfaceCritical;
230     }
231     else if (level == thresholds::Level::CRITICAL &&
232              direction == thresholds::Direction::LOW)
233     {
234         property = "CriticalAlarmLow";
235         interface = sensor->thresholdInterfaceCritical;
236     }
237     else
238     {
239         std::cerr << "Unknown threshold, level " << level << "direction "
240                   << direction << "\n";
241         return;
242     }
243     if (!interface)
244     {
245         std::cout << "trying to set uninitialized interface\n";
246         return;
247     }
248     interface->set_property(property, assert);
249 }
250 
251 static constexpr std::array<const char *, 4> ATTR_TYPES = {"lcrit", "min",
252                                                            "max", "crit"};
253 
254 bool ParseThresholdsFromAttr(
255     std::vector<thresholds::Threshold> &threshold_vector,
256     const std::string &input_path, const double scale_factor)
257 {
258     for (auto &type : ATTR_TYPES)
259     {
260         auto attr_path = boost::replace_all_copy(input_path, "input", type);
261         std::ifstream attr_file(attr_path);
262         if (!attr_file.good())
263             continue;
264         std::string attr;
265         std::getline(attr_file, attr);
266         attr_file.close();
267 
268         Level level;
269         Direction direction;
270         double val = std::stod(attr) / scale_factor;
271         if (type == "min" || type == "max")
272             level = Level::WARNING;
273         else
274             level = Level::CRITICAL;
275         if (type == "min" || type == "lcrit")
276             direction = Direction::LOW;
277         else
278             direction = Direction::HIGH;
279 
280         if (DEBUG)
281             std::cout << "Threshold: " << attr_path << ": " << val << "\n";
282 
283         threshold_vector.emplace_back(level, direction, val);
284     }
285     // no thresholds is allowed, not an error so return true always
286     return true;
287 }
288 
289 bool HasCriticalInterface(
290     const std::vector<thresholds::Threshold> &threshold_vector)
291 {
292     for (auto &threshold : threshold_vector)
293     {
294         if (threshold.level == Level::CRITICAL)
295             return true;
296     }
297     return false;
298 }
299 
300 bool HasWarningInterface(
301     const std::vector<thresholds::Threshold> &threshold_vector)
302 {
303     for (auto &threshold : threshold_vector)
304     {
305         if (threshold.level == Level::WARNING)
306             return true;
307     }
308     return false;
309 }
310 } // namespace thresholds
311