xref: /openbmc/dbus-sensors/src/Thresholds.cpp (revision 445efe33)
1 #include <Thresholds.hpp>
2 #include <VariantVisitors.hpp>
3 #include <boost/algorithm/string/replace.hpp>
4 #include <boost/lexical_cast.hpp>
5 #include <cmath>
6 #include <fstream>
7 #include <iostream>
8 #include <sensor.hpp>
9 
10 static constexpr bool DEBUG = false;
11 namespace thresholds
12 {
13 unsigned int toBusValue(const Level& level)
14 {
15     switch (level)
16     {
17         case (Level::WARNING):
18         {
19             return 0;
20         }
21         case (Level::CRITICAL):
22         {
23             return 1;
24         }
25         default:
26         {
27             return -1;
28         }
29     }
30 }
31 
32 std::string toBusValue(const Direction& direction)
33 {
34     switch (direction)
35     {
36         case (Direction::LOW):
37         {
38             return "less than";
39         }
40         case (Direction::HIGH):
41         {
42             return "greater than";
43         }
44         default:
45         {
46             return "err";
47         }
48     }
49 }
50 
51 bool parseThresholdsFromConfig(
52     const SensorData& sensorData,
53     std::vector<thresholds::Threshold>& thresholdVector,
54     const std::string* matchLabel)
55 {
56     for (const auto& item : sensorData)
57     {
58         if (item.first.find("Thresholds") == std::string::npos)
59         {
60             continue;
61         }
62         if (matchLabel != nullptr)
63         {
64             auto labelFind = item.second.find("Label");
65             if (labelFind == item.second.end())
66                 continue;
67             if (std::visit(VariantToStringVisitor(), labelFind->second) !=
68                 *matchLabel)
69                 continue;
70         }
71         auto directionFind = item.second.find("Direction");
72         auto severityFind = item.second.find("Severity");
73         auto valueFind = item.second.find("Value");
74         if (valueFind == item.second.end() ||
75             severityFind == item.second.end() ||
76             directionFind == item.second.end())
77         {
78             std::cerr << "Malformed threshold in configuration\n";
79             return false;
80         }
81         Level level;
82         Direction direction;
83         if (std::visit(VariantToUnsignedIntVisitor(), severityFind->second) ==
84             0)
85         {
86             level = Level::WARNING;
87         }
88         else
89         {
90             level = Level::CRITICAL;
91         }
92         if (std::visit(VariantToStringVisitor(), directionFind->second) ==
93             "less than")
94         {
95             direction = Direction::LOW;
96         }
97         else
98         {
99             direction = Direction::HIGH;
100         }
101         double val = std::visit(VariantToDoubleVisitor(), valueFind->second);
102 
103         thresholdVector.emplace_back(level, direction, val);
104     }
105     return true;
106 }
107 
108 void persistThreshold(const std::string& path, const std::string& baseInterface,
109                       const thresholds::Threshold& threshold,
110                       std::shared_ptr<sdbusplus::asio::connection>& conn,
111                       size_t thresholdCount)
112 {
113     for (int ii = 0; ii < thresholdCount; ii++)
114     {
115         std::string thresholdInterface =
116             baseInterface + ".Thresholds" + std::to_string(ii);
117         conn->async_method_call(
118             [&, path, threshold, thresholdInterface](
119                 const boost::system::error_code& ec,
120                 const boost::container::flat_map<std::string, BasicVariantType>&
121                     result) {
122                 if (ec)
123                 {
124                     return; // threshold not supported
125                 }
126 
127                 auto directionFind = result.find("Direction");
128                 auto severityFind = result.find("Severity");
129                 auto valueFind = result.find("Value");
130                 if (valueFind == result.end() || severityFind == result.end() ||
131                     directionFind == result.end())
132                 {
133                     std::cerr << "Malformed threshold in configuration\n";
134                     return;
135                 }
136                 unsigned int level = std::visit(VariantToUnsignedIntVisitor(),
137                                                 severityFind->second);
138 
139                 std::string dir =
140                     std::visit(VariantToStringVisitor(), directionFind->second);
141                 if ((toBusValue(threshold.level) != level) ||
142                     (toBusValue(threshold.direction) != dir))
143                 {
144                     return; // not the droid we're looking for
145                 }
146 
147                 std::variant<double> value(threshold.value);
148                 conn->async_method_call(
149                     [](const boost::system::error_code& ec) {
150                         if (ec)
151                         {
152                             std::cerr << "Error setting threshold " << ec
153                                       << "\n";
154                         }
155                     },
156                     entityManagerName, path, "org.freedesktop.DBus.Properties",
157                     "Set", thresholdInterface, "Value", value);
158             },
159             entityManagerName, path, "org.freedesktop.DBus.Properties",
160             "GetAll", thresholdInterface);
161     }
162 }
163 
164 void updateThresholds(Sensor* sensor)
165 {
166     if (sensor->thresholds.empty())
167     {
168         return;
169     }
170 
171     for (const auto& threshold : sensor->thresholds)
172     {
173         std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
174         std::string property;
175         if (threshold.level == thresholds::Level::CRITICAL)
176         {
177             interface = sensor->thresholdInterfaceCritical;
178             if (threshold.direction == thresholds::Direction::HIGH)
179             {
180                 property = "CriticalHigh";
181             }
182             else
183             {
184                 property = "CriticalLow";
185             }
186         }
187         else if (threshold.level == thresholds::Level::WARNING)
188         {
189             interface = sensor->thresholdInterfaceWarning;
190             if (threshold.direction == thresholds::Direction::HIGH)
191             {
192                 property = "WarningHigh";
193             }
194             else
195             {
196                 property = "WarningLow";
197             }
198         }
199         else
200         {
201             continue;
202         }
203         if (!interface)
204         {
205             continue;
206         }
207         interface->set_property(property, threshold.value);
208     }
209 }
210 
211 static std::vector<std::pair<Threshold, bool>> checkThresholds(Sensor* sensor,
212                                                                double value)
213 {
214     std::vector<std::pair<Threshold, bool>> thresholdChanges;
215     if (sensor->thresholds.empty())
216     {
217         return thresholdChanges;
218     }
219 
220     for (auto& threshold : sensor->thresholds)
221     {
222         if (threshold.direction == thresholds::Direction::HIGH)
223         {
224             if (value > threshold.value)
225             {
226                 thresholdChanges.push_back(std::make_pair(threshold, true));
227             }
228             else if (value <= threshold.value)
229             {
230                 thresholdChanges.push_back(std::make_pair(threshold, false));
231             }
232         }
233         else
234         {
235             if (value < threshold.value)
236             {
237                 thresholdChanges.push_back(std::make_pair(threshold, true));
238             }
239             else if (value >= threshold.value)
240             {
241                 thresholdChanges.push_back(std::make_pair(threshold, false));
242             }
243         }
244     }
245     return thresholdChanges;
246 }
247 
248 bool checkThresholds(Sensor* sensor)
249 {
250     bool status = true;
251     std::vector<std::pair<Threshold, bool>> changes =
252         checkThresholds(sensor, sensor->value);
253     for (const auto& [threshold, asserted] : changes)
254     {
255         assertThresholds(sensor, threshold.level, threshold.direction,
256                          asserted);
257         if (threshold.level == thresholds::Level::CRITICAL && asserted)
258         {
259             status = false;
260         }
261     }
262 
263     return status;
264 }
265 
266 void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer)
267 {
268 
269     std::vector<std::pair<Threshold, bool>> changes =
270         checkThresholds(sensor, sensor->value);
271     for (const auto& [threshold, asserted] : changes)
272     {
273         if (asserted)
274         {
275             thresholdTimer.startTimer(threshold);
276         }
277         else
278         {
279             assertThresholds(sensor, threshold.level, threshold.direction,
280                              false);
281         }
282     }
283 }
284 
285 void assertThresholds(Sensor* sensor, thresholds::Level level,
286                       thresholds::Direction direction, bool assert)
287 {
288     std::string property;
289     std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
290     if (level == thresholds::Level::WARNING &&
291         direction == thresholds::Direction::HIGH)
292     {
293         property = "WarningAlarmHigh";
294         interface = sensor->thresholdInterfaceWarning;
295     }
296     else if (level == thresholds::Level::WARNING &&
297              direction == thresholds::Direction::LOW)
298     {
299         property = "WarningAlarmLow";
300         interface = sensor->thresholdInterfaceWarning;
301     }
302     else if (level == thresholds::Level::CRITICAL &&
303              direction == thresholds::Direction::HIGH)
304     {
305         property = "CriticalAlarmHigh";
306         interface = sensor->thresholdInterfaceCritical;
307     }
308     else if (level == thresholds::Level::CRITICAL &&
309              direction == thresholds::Direction::LOW)
310     {
311         property = "CriticalAlarmLow";
312         interface = sensor->thresholdInterfaceCritical;
313     }
314     else
315     {
316         std::cerr << "Unknown threshold, level " << level << "direction "
317                   << direction << "\n";
318         return;
319     }
320     if (!interface)
321     {
322         std::cout << "trying to set uninitialized interface\n";
323         return;
324     }
325     interface->set_property(property, assert);
326 }
327 
328 static constexpr std::array<const char*, 4> attrTypes = {"lcrit", "min", "max",
329                                                          "crit"};
330 
331 bool parseThresholdsFromAttr(
332     std::vector<thresholds::Threshold>& thresholdVector,
333     const std::string& inputPath, const double& scaleFactor,
334     const double& offset)
335 {
336     for (auto& type : attrTypes)
337     {
338         auto attrPath = boost::replace_all_copy(inputPath, "input", type);
339         std::ifstream attrFile(attrPath);
340         if (!attrFile.good())
341         {
342             continue;
343         }
344         std::string attr;
345         std::getline(attrFile, attr);
346         attrFile.close();
347 
348         Level level;
349         Direction direction;
350         double val = std::stod(attr) / scaleFactor;
351         if (type == "min" || type == "max")
352         {
353             level = Level::WARNING;
354         }
355         else
356         {
357             level = Level::CRITICAL;
358         }
359         if (type == "min" || type == "lcrit")
360         {
361             direction = Direction::LOW;
362         }
363         else
364         {
365             direction = Direction::HIGH;
366         }
367 
368         if (type == "crit")
369         {
370             val += offset;
371         }
372 
373         if (DEBUG)
374         {
375             std::cout << "Threshold: " << attrPath << ": " << val << "\n";
376         }
377 
378         thresholdVector.emplace_back(level, direction, val);
379     }
380     // no thresholds is allowed, not an error so return true always
381     return true;
382 }
383 
384 bool hasCriticalInterface(
385     const std::vector<thresholds::Threshold>& thresholdVector)
386 {
387     for (auto& threshold : thresholdVector)
388     {
389         if (threshold.level == Level::CRITICAL)
390         {
391             return true;
392         }
393     }
394     return false;
395 }
396 
397 bool hasWarningInterface(
398     const std::vector<thresholds::Threshold>& thresholdVector)
399 {
400     for (auto& threshold : thresholdVector)
401     {
402         if (threshold.level == Level::WARNING)
403         {
404             return true;
405         }
406     }
407     return false;
408 }
409 } // namespace thresholds
410