blob: adc1f68c8a9c438dff3ab2c9e91bc723011a8465 [file] [log] [blame]
Patrick Ventureca44b2f2019-10-31 11:02:26 -07001#include "Thresholds.hpp"
2
3#include "VariantVisitors.hpp"
4#include "sensor.hpp"
5
James Feist6714a252018-09-10 15:26:18 -07006#include <boost/algorithm/string/replace.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -07007#include <boost/container/flat_map.hpp>
James Feist6714a252018-09-10 15:26:18 -07008#include <boost/lexical_cast.hpp>
James Feist38fb5982020-05-28 10:09:54 -07009
10#include <array>
James Feistdc6c55f2018-10-31 12:53:20 -070011#include <cmath>
James Feist6714a252018-09-10 15:26:18 -070012#include <fstream>
13#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070014#include <memory>
15#include <stdexcept>
16#include <string>
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +020017#include <tuple>
Patrick Venture96e97db2019-10-31 13:44:38 -070018#include <utility>
19#include <variant>
20#include <vector>
James Feist6714a252018-09-10 15:26:18 -070021
22static constexpr bool DEBUG = false;
James Feist6714a252018-09-10 15:26:18 -070023namespace thresholds
24{
James Feistd8705872019-02-08 13:26:09 -080025unsigned int toBusValue(const Level& level)
James Feist6714a252018-09-10 15:26:18 -070026{
27 switch (level)
28 {
29 case (Level::WARNING):
30 {
31 return 0;
32 }
33 case (Level::CRITICAL):
34 {
35 return 1;
36 }
37 default:
38 {
39 return -1;
40 }
41 }
42}
43
James Feistd8705872019-02-08 13:26:09 -080044std::string toBusValue(const Direction& direction)
James Feist6714a252018-09-10 15:26:18 -070045{
46 switch (direction)
47 {
48 case (Direction::LOW):
49 {
50 return "less than";
51 }
52 case (Direction::HIGH):
53 {
54 return "greater than";
55 }
56 default:
57 {
58 return "err";
59 }
60 }
61}
62
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070063bool parseThresholdsFromConfig(
James Feistd8705872019-02-08 13:26:09 -080064 const SensorData& sensorData,
65 std::vector<thresholds::Threshold>& thresholdVector,
66 const std::string* matchLabel)
James Feist6714a252018-09-10 15:26:18 -070067{
James Feistd8705872019-02-08 13:26:09 -080068 for (const auto& item : sensorData)
James Feist6714a252018-09-10 15:26:18 -070069 {
70 if (item.first.find("Thresholds") == std::string::npos)
71 {
72 continue;
73 }
74 if (matchLabel != nullptr)
75 {
76 auto labelFind = item.second.find("Label");
77 if (labelFind == item.second.end())
78 continue;
James Feist3eb82622019-02-08 13:10:22 -080079 if (std::visit(VariantToStringVisitor(), labelFind->second) !=
80 *matchLabel)
James Feist6714a252018-09-10 15:26:18 -070081 continue;
82 }
83 auto directionFind = item.second.find("Direction");
84 auto severityFind = item.second.find("Severity");
85 auto valueFind = item.second.find("Value");
86 if (valueFind == item.second.end() ||
87 severityFind == item.second.end() ||
88 directionFind == item.second.end())
89 {
90 std::cerr << "Malformed threshold in configuration\n";
91 return false;
92 }
93 Level level;
94 Direction direction;
James Feist3eb82622019-02-08 13:10:22 -080095 if (std::visit(VariantToUnsignedIntVisitor(), severityFind->second) ==
96 0)
James Feist6714a252018-09-10 15:26:18 -070097 {
98 level = Level::WARNING;
99 }
100 else
101 {
102 level = Level::CRITICAL;
103 }
James Feist3eb82622019-02-08 13:10:22 -0800104 if (std::visit(VariantToStringVisitor(), directionFind->second) ==
105 "less than")
James Feist6714a252018-09-10 15:26:18 -0700106 {
107 direction = Direction::LOW;
108 }
109 else
110 {
111 direction = Direction::HIGH;
112 }
James Feist13f340b2019-03-07 16:36:11 -0800113 double val = std::visit(VariantToDoubleVisitor(), valueFind->second);
James Feist6714a252018-09-10 15:26:18 -0700114
115 thresholdVector.emplace_back(level, direction, val);
116 }
117 return true;
118}
119
James Feistd8705872019-02-08 13:26:09 -0800120void persistThreshold(const std::string& path, const std::string& baseInterface,
121 const thresholds::Threshold& threshold,
James Feista222ba72019-03-01 15:57:51 -0800122 std::shared_ptr<sdbusplus::asio::connection>& conn,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800123 size_t thresholdCount, const std::string& labelMatch)
James Feist6714a252018-09-10 15:26:18 -0700124{
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500125 for (size_t ii = 0; ii < thresholdCount; ii++)
James Feist6714a252018-09-10 15:26:18 -0700126 {
127 std::string thresholdInterface =
128 baseInterface + ".Thresholds" + std::to_string(ii);
129 conn->async_method_call(
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800130 [&, path, threshold, thresholdInterface, labelMatch](
James Feistd8705872019-02-08 13:26:09 -0800131 const boost::system::error_code& ec,
132 const boost::container::flat_map<std::string, BasicVariantType>&
133 result) {
James Feist6714a252018-09-10 15:26:18 -0700134 if (ec)
135 {
136 return; // threshold not supported
137 }
138
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800139 if (!labelMatch.empty())
140 {
141 auto labelFind = result.find("Label");
142 if (labelFind == result.end())
143 {
144 std::cerr << "No label in threshold configuration\n";
145 return;
146 }
147 std::string label =
148 std::visit(VariantToStringVisitor(), labelFind->second);
149 if (label != labelMatch)
150 {
151 return;
152 }
153 }
154
James Feist6714a252018-09-10 15:26:18 -0700155 auto directionFind = result.find("Direction");
156 auto severityFind = result.find("Severity");
157 auto valueFind = result.find("Value");
158 if (valueFind == result.end() || severityFind == result.end() ||
159 directionFind == result.end())
160 {
161 std::cerr << "Malformed threshold in configuration\n";
162 return;
163 }
James Feist3eb82622019-02-08 13:10:22 -0800164 unsigned int level = std::visit(VariantToUnsignedIntVisitor(),
165 severityFind->second);
James Feist6714a252018-09-10 15:26:18 -0700166
James Feist3eb82622019-02-08 13:10:22 -0800167 std::string dir =
168 std::visit(VariantToStringVisitor(), directionFind->second);
James Feist6714a252018-09-10 15:26:18 -0700169 if ((toBusValue(threshold.level) != level) ||
170 (toBusValue(threshold.direction) != dir))
171 {
172 return; // not the droid we're looking for
173 }
174
James Feist3eb82622019-02-08 13:10:22 -0800175 std::variant<double> value(threshold.value);
James Feist6714a252018-09-10 15:26:18 -0700176 conn->async_method_call(
James Feistd8705872019-02-08 13:26:09 -0800177 [](const boost::system::error_code& ec) {
James Feist6714a252018-09-10 15:26:18 -0700178 if (ec)
179 {
180 std::cerr << "Error setting threshold " << ec
181 << "\n";
182 }
183 },
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700184 entityManagerName, path, "org.freedesktop.DBus.Properties",
185 "Set", thresholdInterface, "Value", value);
James Feist6714a252018-09-10 15:26:18 -0700186 },
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700187 entityManagerName, path, "org.freedesktop.DBus.Properties",
James Feist6714a252018-09-10 15:26:18 -0700188 "GetAll", thresholdInterface);
189 }
190}
191
Jae Hyun Yoo95b8a2d2019-02-25 20:15:09 -0800192void updateThresholds(Sensor* sensor)
193{
194 if (sensor->thresholds.empty())
195 {
196 return;
197 }
198
199 for (const auto& threshold : sensor->thresholds)
200 {
201 std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
202 std::string property;
203 if (threshold.level == thresholds::Level::CRITICAL)
204 {
205 interface = sensor->thresholdInterfaceCritical;
206 if (threshold.direction == thresholds::Direction::HIGH)
207 {
208 property = "CriticalHigh";
209 }
210 else
211 {
212 property = "CriticalLow";
213 }
214 }
215 else if (threshold.level == thresholds::Level::WARNING)
216 {
217 interface = sensor->thresholdInterfaceWarning;
218 if (threshold.direction == thresholds::Direction::HIGH)
219 {
220 property = "WarningHigh";
221 }
222 else
223 {
224 property = "WarningLow";
225 }
226 }
227 else
228 {
229 continue;
230 }
231 if (!interface)
232 {
233 continue;
234 }
235 interface->set_property(property, threshold.value);
236 }
237}
238
Josh Lehan883fb3a2020-02-27 14:41:39 -0800239// Debugging counters
240static int cHiTrue = 0;
241static int cHiFalse = 0;
242static int cHiMidstate = 0;
243static int cLoTrue = 0;
244static int cLoFalse = 0;
245static int cLoMidstate = 0;
246static int cDebugThrottle = 0;
247
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700248struct ChangeParam
James Feist251c7822018-09-12 12:54:15 -0700249{
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700250 ChangeParam(Threshold whichThreshold, bool status, double value) :
251 threshold(whichThreshold), asserted(status), assertValue(value)
252 {}
253
254 Threshold threshold;
255 bool asserted;
256 double assertValue;
257};
258
259static std::vector<ChangeParam> checkThresholds(Sensor* sensor, double value)
260{
261 std::vector<ChangeParam> thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700262 if (sensor->thresholds.empty())
263 {
James Feist46342ec2019-03-06 14:03:41 -0800264 return thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700265 }
James Feist46342ec2019-03-06 14:03:41 -0800266
James Feistd8705872019-02-08 13:26:09 -0800267 for (auto& threshold : sensor->thresholds)
James Feist251c7822018-09-12 12:54:15 -0700268 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800269 // Use "Schmitt trigger" logic to avoid threshold trigger spam,
270 // if value is noisy while hovering very close to a threshold.
271 // When a threshold is crossed, indicate true immediately,
272 // but require more distance to be crossed the other direction,
273 // before resetting the indicator back to false.
James Feist46342ec2019-03-06 14:03:41 -0800274 if (threshold.direction == thresholds::Direction::HIGH)
James Feistdc6c55f2018-10-31 12:53:20 -0700275 {
James Feist551087a2019-12-09 11:17:12 -0800276 if (value >= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700277 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700278 thresholdChanges.emplace_back(threshold, true, value);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800279 ++cHiTrue;
280 }
281 else if (value < (threshold.value - sensor->hysteresisTrigger))
282 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700283 thresholdChanges.emplace_back(threshold, false, value);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800284 ++cHiFalse;
James Feist251c7822018-09-12 12:54:15 -0700285 }
Patrick Venture66235d42019-10-11 08:31:27 -0700286 else
James Feist251c7822018-09-12 12:54:15 -0700287 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800288 ++cHiMidstate;
James Feist251c7822018-09-12 12:54:15 -0700289 }
290 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800291 else if (threshold.direction == thresholds::Direction::LOW)
James Feist251c7822018-09-12 12:54:15 -0700292 {
James Feist551087a2019-12-09 11:17:12 -0800293 if (value <= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700294 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700295 thresholdChanges.emplace_back(threshold, true, value);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800296 ++cLoTrue;
297 }
298 else if (value > (threshold.value + sensor->hysteresisTrigger))
299 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700300 thresholdChanges.emplace_back(threshold, false, value);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800301 ++cLoFalse;
James Feist251c7822018-09-12 12:54:15 -0700302 }
Patrick Venture66235d42019-10-11 08:31:27 -0700303 else
James Feist251c7822018-09-12 12:54:15 -0700304 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800305 ++cLoMidstate;
James Feist251c7822018-09-12 12:54:15 -0700306 }
307 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800308 else
309 {
310 std::cerr << "Error determining threshold direction\n";
311 }
James Feist251c7822018-09-12 12:54:15 -0700312 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800313
314 if constexpr (DEBUG)
315 {
316 // Throttle debug output, so that it does not continuously spam
317 ++cDebugThrottle;
318 if (cDebugThrottle >= 1000)
319 {
320 cDebugThrottle = 0;
321 std::cerr << "checkThresholds: High T=" << cHiTrue
322 << " F=" << cHiFalse << " M=" << cHiMidstate
323 << ", Low T=" << cLoTrue << " F=" << cLoFalse
324 << " M=" << cLoMidstate << "\n";
325 }
326 }
327
James Feist46342ec2019-03-06 14:03:41 -0800328 return thresholdChanges;
329}
330
331bool checkThresholds(Sensor* sensor)
332{
James Feist7b18b1e2019-05-14 13:42:09 -0700333 bool status = true;
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700334 std::vector<ChangeParam> changes = checkThresholds(sensor, sensor->value);
335 for (const auto& change : changes)
James Feist46342ec2019-03-06 14:03:41 -0800336 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700337 assertThresholds(sensor, change.assertValue, change.threshold.level,
338 change.threshold.direction, change.asserted);
339 if (change.threshold.level == thresholds::Level::CRITICAL &&
340 change.asserted)
James Feist46342ec2019-03-06 14:03:41 -0800341 {
James Feist7b18b1e2019-05-14 13:42:09 -0700342 status = false;
James Feist46342ec2019-03-06 14:03:41 -0800343 }
344 }
345
James Feistdc6c55f2018-10-31 12:53:20 -0700346 return status;
James Feist251c7822018-09-12 12:54:15 -0700347}
348
James Feist46342ec2019-03-06 14:03:41 -0800349void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer)
350{
351
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700352 std::vector<ChangeParam> changes = checkThresholds(sensor, sensor->value);
353 for (const auto& change : changes)
James Feist46342ec2019-03-06 14:03:41 -0800354 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700355 if (change.asserted)
James Feist46342ec2019-03-06 14:03:41 -0800356 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700357 thresholdTimer.startTimer(change.threshold, change.assertValue);
James Feist46342ec2019-03-06 14:03:41 -0800358 }
359 else
360 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700361 thresholdTimer.stopTimer(change.threshold);
362 assertThresholds(sensor, change.assertValue, change.threshold.level,
363 change.threshold.direction, false);
James Feist46342ec2019-03-06 14:03:41 -0800364 }
365 }
366}
367
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700368void assertThresholds(Sensor* sensor, double assertValue,
369 thresholds::Level level, thresholds::Direction direction,
370 bool assert)
James Feist251c7822018-09-12 12:54:15 -0700371{
372 std::string property;
373 std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
374 if (level == thresholds::Level::WARNING &&
375 direction == thresholds::Direction::HIGH)
376 {
377 property = "WarningAlarmHigh";
378 interface = sensor->thresholdInterfaceWarning;
379 }
380 else if (level == thresholds::Level::WARNING &&
381 direction == thresholds::Direction::LOW)
382 {
383 property = "WarningAlarmLow";
384 interface = sensor->thresholdInterfaceWarning;
385 }
386 else if (level == thresholds::Level::CRITICAL &&
387 direction == thresholds::Direction::HIGH)
388 {
389 property = "CriticalAlarmHigh";
390 interface = sensor->thresholdInterfaceCritical;
391 }
392 else if (level == thresholds::Level::CRITICAL &&
393 direction == thresholds::Direction::LOW)
394 {
395 property = "CriticalAlarmLow";
396 interface = sensor->thresholdInterfaceCritical;
397 }
398 else
399 {
400 std::cerr << "Unknown threshold, level " << level << "direction "
401 << direction << "\n";
402 return;
403 }
404 if (!interface)
405 {
406 std::cout << "trying to set uninitialized interface\n";
407 return;
408 }
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700409
410 if (interface->set_property<bool, true>(property, assert))
411 {
412 try
413 {
414 // msg.get_path() is interface->get_object_path()
415 sdbusplus::message::message msg =
416 interface->new_signal("ThresholdAsserted");
417
418 msg.append(sensor->name, interface->get_interface_name(), property,
419 assert, assertValue);
420 msg.signal_send();
421 }
422 catch (const sdbusplus::exception::exception& e)
423 {
424 std::cerr
425 << "Failed to send thresholdAsserted signal with assertValue\n";
426 }
427 }
James Feist251c7822018-09-12 12:54:15 -0700428}
429
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700430bool parseThresholdsFromAttr(
James Feistd8705872019-02-08 13:26:09 -0800431 std::vector<thresholds::Threshold>& thresholdVector,
Vijay Khemka86dea2b2019-06-06 11:14:37 -0700432 const std::string& inputPath, const double& scaleFactor,
433 const double& offset)
James Feist6714a252018-09-10 15:26:18 -0700434{
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200435 const boost::container::flat_map<
436 std::string, std::vector<std::tuple<const char*, thresholds::Level,
437 thresholds::Direction, double>>>
438 map = {
439 {"average",
440 {
441 std::make_tuple("average_min", Level::WARNING, Direction::LOW,
442 0.0),
443 std::make_tuple("average_max", Level::WARNING, Direction::HIGH,
444 0.0),
445 }},
446 {"input",
447 {
448 std::make_tuple("min", Level::WARNING, Direction::LOW, 0.0),
449 std::make_tuple("max", Level::WARNING, Direction::HIGH, 0.0),
450 std::make_tuple("lcrit", Level::CRITICAL, Direction::LOW, 0.0),
451 std::make_tuple("crit", Level::CRITICAL, Direction::HIGH,
452 offset),
453 }},
454 };
455
456 if (auto fileParts = splitFileName(inputPath))
James Feist6714a252018-09-10 15:26:18 -0700457 {
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200458 auto [type, nr, item] = *fileParts;
459 if (map.count(item) != 0)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700460 {
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200461 for (const auto& t : map.at(item))
462 {
463 auto [suffix, level, direction, offset] = t;
464 auto attrPath =
465 boost::replace_all_copy(inputPath, item, suffix);
466 if (auto val = readFile(attrPath, scaleFactor))
467 {
468 *val += offset;
469 if (DEBUG)
470 {
471 std::cout << "Threshold: " << attrPath << ": " << *val
472 << "\n";
473 }
474 thresholdVector.emplace_back(level, direction, *val);
475 }
476 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700477 }
James Feist6714a252018-09-10 15:26:18 -0700478 }
James Feist6714a252018-09-10 15:26:18 -0700479 return true;
480}
481
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700482bool hasCriticalInterface(
James Feistd8705872019-02-08 13:26:09 -0800483 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700484{
James Feistd8705872019-02-08 13:26:09 -0800485 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700486 {
487 if (threshold.level == Level::CRITICAL)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700488 {
James Feist6714a252018-09-10 15:26:18 -0700489 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700490 }
James Feist6714a252018-09-10 15:26:18 -0700491 }
492 return false;
493}
494
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700495bool hasWarningInterface(
James Feistd8705872019-02-08 13:26:09 -0800496 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700497{
James Feistd8705872019-02-08 13:26:09 -0800498 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700499 {
500 if (threshold.level == Level::WARNING)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700501 {
James Feist6714a252018-09-10 15:26:18 -0700502 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700503 }
James Feist6714a252018-09-10 15:26:18 -0700504 }
505 return false;
506}
507} // namespace thresholds