blob: 61f951ba7df3d4f05621ce56d0152c7691c02b50 [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
James Feist46342ec2019-03-06 14:03:41 -0800248static std::vector<std::pair<Threshold, bool>> checkThresholds(Sensor* sensor,
249 double value)
James Feist251c7822018-09-12 12:54:15 -0700250{
James Feist46342ec2019-03-06 14:03:41 -0800251 std::vector<std::pair<Threshold, bool>> thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700252 if (sensor->thresholds.empty())
253 {
James Feist46342ec2019-03-06 14:03:41 -0800254 return thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700255 }
James Feist46342ec2019-03-06 14:03:41 -0800256
James Feistd8705872019-02-08 13:26:09 -0800257 for (auto& threshold : sensor->thresholds)
James Feist251c7822018-09-12 12:54:15 -0700258 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800259 // Use "Schmitt trigger" logic to avoid threshold trigger spam,
260 // if value is noisy while hovering very close to a threshold.
261 // When a threshold is crossed, indicate true immediately,
262 // but require more distance to be crossed the other direction,
263 // before resetting the indicator back to false.
James Feist46342ec2019-03-06 14:03:41 -0800264 if (threshold.direction == thresholds::Direction::HIGH)
James Feistdc6c55f2018-10-31 12:53:20 -0700265 {
James Feist551087a2019-12-09 11:17:12 -0800266 if (value >= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700267 {
James Feist46342ec2019-03-06 14:03:41 -0800268 thresholdChanges.push_back(std::make_pair(threshold, true));
Josh Lehan883fb3a2020-02-27 14:41:39 -0800269 ++cHiTrue;
270 }
271 else if (value < (threshold.value - sensor->hysteresisTrigger))
272 {
273 thresholdChanges.push_back(std::make_pair(threshold, false));
274 ++cHiFalse;
James Feist251c7822018-09-12 12:54:15 -0700275 }
Patrick Venture66235d42019-10-11 08:31:27 -0700276 else
James Feist251c7822018-09-12 12:54:15 -0700277 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800278 ++cHiMidstate;
James Feist251c7822018-09-12 12:54:15 -0700279 }
280 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800281 else if (threshold.direction == thresholds::Direction::LOW)
James Feist251c7822018-09-12 12:54:15 -0700282 {
James Feist551087a2019-12-09 11:17:12 -0800283 if (value <= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700284 {
James Feist46342ec2019-03-06 14:03:41 -0800285 thresholdChanges.push_back(std::make_pair(threshold, true));
Josh Lehan883fb3a2020-02-27 14:41:39 -0800286 ++cLoTrue;
287 }
288 else if (value > (threshold.value + sensor->hysteresisTrigger))
289 {
290 thresholdChanges.push_back(std::make_pair(threshold, false));
291 ++cLoFalse;
James Feist251c7822018-09-12 12:54:15 -0700292 }
Patrick Venture66235d42019-10-11 08:31:27 -0700293 else
James Feist251c7822018-09-12 12:54:15 -0700294 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800295 ++cLoMidstate;
James Feist251c7822018-09-12 12:54:15 -0700296 }
297 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800298 else
299 {
300 std::cerr << "Error determining threshold direction\n";
301 }
James Feist251c7822018-09-12 12:54:15 -0700302 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800303
304 if constexpr (DEBUG)
305 {
306 // Throttle debug output, so that it does not continuously spam
307 ++cDebugThrottle;
308 if (cDebugThrottle >= 1000)
309 {
310 cDebugThrottle = 0;
311 std::cerr << "checkThresholds: High T=" << cHiTrue
312 << " F=" << cHiFalse << " M=" << cHiMidstate
313 << ", Low T=" << cLoTrue << " F=" << cLoFalse
314 << " M=" << cLoMidstate << "\n";
315 }
316 }
317
James Feist46342ec2019-03-06 14:03:41 -0800318 return thresholdChanges;
319}
320
321bool checkThresholds(Sensor* sensor)
322{
James Feist7b18b1e2019-05-14 13:42:09 -0700323 bool status = true;
James Feist46342ec2019-03-06 14:03:41 -0800324 std::vector<std::pair<Threshold, bool>> changes =
325 checkThresholds(sensor, sensor->value);
326 for (const auto& [threshold, asserted] : changes)
327 {
328 assertThresholds(sensor, threshold.level, threshold.direction,
329 asserted);
330 if (threshold.level == thresholds::Level::CRITICAL && asserted)
331 {
James Feist7b18b1e2019-05-14 13:42:09 -0700332 status = false;
James Feist46342ec2019-03-06 14:03:41 -0800333 }
334 }
335
James Feistdc6c55f2018-10-31 12:53:20 -0700336 return status;
James Feist251c7822018-09-12 12:54:15 -0700337}
338
James Feist46342ec2019-03-06 14:03:41 -0800339void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer)
340{
341
342 std::vector<std::pair<Threshold, bool>> changes =
343 checkThresholds(sensor, sensor->value);
344 for (const auto& [threshold, asserted] : changes)
345 {
346 if (asserted)
347 {
348 thresholdTimer.startTimer(threshold);
349 }
350 else
351 {
Yong Li10306bd2020-04-21 16:08:28 +0800352 thresholdTimer.stopTimer(threshold);
James Feist46342ec2019-03-06 14:03:41 -0800353 assertThresholds(sensor, threshold.level, threshold.direction,
354 false);
355 }
356 }
357}
358
James Feistd8705872019-02-08 13:26:09 -0800359void assertThresholds(Sensor* sensor, thresholds::Level level,
James Feist251c7822018-09-12 12:54:15 -0700360 thresholds::Direction direction, bool assert)
361{
362 std::string property;
363 std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
364 if (level == thresholds::Level::WARNING &&
365 direction == thresholds::Direction::HIGH)
366 {
367 property = "WarningAlarmHigh";
368 interface = sensor->thresholdInterfaceWarning;
369 }
370 else if (level == thresholds::Level::WARNING &&
371 direction == thresholds::Direction::LOW)
372 {
373 property = "WarningAlarmLow";
374 interface = sensor->thresholdInterfaceWarning;
375 }
376 else if (level == thresholds::Level::CRITICAL &&
377 direction == thresholds::Direction::HIGH)
378 {
379 property = "CriticalAlarmHigh";
380 interface = sensor->thresholdInterfaceCritical;
381 }
382 else if (level == thresholds::Level::CRITICAL &&
383 direction == thresholds::Direction::LOW)
384 {
385 property = "CriticalAlarmLow";
386 interface = sensor->thresholdInterfaceCritical;
387 }
388 else
389 {
390 std::cerr << "Unknown threshold, level " << level << "direction "
391 << direction << "\n";
392 return;
393 }
394 if (!interface)
395 {
396 std::cout << "trying to set uninitialized interface\n";
397 return;
398 }
399 interface->set_property(property, assert);
400}
401
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700402bool parseThresholdsFromAttr(
James Feistd8705872019-02-08 13:26:09 -0800403 std::vector<thresholds::Threshold>& thresholdVector,
Vijay Khemka86dea2b2019-06-06 11:14:37 -0700404 const std::string& inputPath, const double& scaleFactor,
405 const double& offset)
James Feist6714a252018-09-10 15:26:18 -0700406{
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200407 const boost::container::flat_map<
408 std::string, std::vector<std::tuple<const char*, thresholds::Level,
409 thresholds::Direction, double>>>
410 map = {
411 {"average",
412 {
413 std::make_tuple("average_min", Level::WARNING, Direction::LOW,
414 0.0),
415 std::make_tuple("average_max", Level::WARNING, Direction::HIGH,
416 0.0),
417 }},
418 {"input",
419 {
420 std::make_tuple("min", Level::WARNING, Direction::LOW, 0.0),
421 std::make_tuple("max", Level::WARNING, Direction::HIGH, 0.0),
422 std::make_tuple("lcrit", Level::CRITICAL, Direction::LOW, 0.0),
423 std::make_tuple("crit", Level::CRITICAL, Direction::HIGH,
424 offset),
425 }},
426 };
427
428 if (auto fileParts = splitFileName(inputPath))
James Feist6714a252018-09-10 15:26:18 -0700429 {
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200430 auto [type, nr, item] = *fileParts;
431 if (map.count(item) != 0)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700432 {
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200433 for (const auto& t : map.at(item))
434 {
435 auto [suffix, level, direction, offset] = t;
436 auto attrPath =
437 boost::replace_all_copy(inputPath, item, suffix);
438 if (auto val = readFile(attrPath, scaleFactor))
439 {
440 *val += offset;
441 if (DEBUG)
442 {
443 std::cout << "Threshold: " << attrPath << ": " << *val
444 << "\n";
445 }
446 thresholdVector.emplace_back(level, direction, *val);
447 }
448 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700449 }
James Feist6714a252018-09-10 15:26:18 -0700450 }
James Feist6714a252018-09-10 15:26:18 -0700451 return true;
452}
453
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700454bool hasCriticalInterface(
James Feistd8705872019-02-08 13:26:09 -0800455 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700456{
James Feistd8705872019-02-08 13:26:09 -0800457 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700458 {
459 if (threshold.level == Level::CRITICAL)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700460 {
James Feist6714a252018-09-10 15:26:18 -0700461 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700462 }
James Feist6714a252018-09-10 15:26:18 -0700463 }
464 return false;
465}
466
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700467bool hasWarningInterface(
James Feistd8705872019-02-08 13:26:09 -0800468 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700469{
James Feistd8705872019-02-08 13:26:09 -0800470 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700471 {
472 if (threshold.level == Level::WARNING)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700473 {
James Feist6714a252018-09-10 15:26:18 -0700474 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700475 }
James Feist6714a252018-09-10 15:26:18 -0700476 }
477 return false;
478}
479} // namespace thresholds