blob: b5e8685871c674fb90a8de45ed8e3517c4f7ef22 [file] [log] [blame]
Patrick Ventureca44b2f2019-10-31 11:02:26 -07001#include "Thresholds.hpp"
2
3#include "VariantVisitors.hpp"
4#include "sensor.hpp"
5
Patrick Venture96e97db2019-10-31 13:44:38 -07006#include <array>
James Feist6714a252018-09-10 15:26:18 -07007#include <boost/algorithm/string/replace.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -07008#include <boost/container/flat_map.hpp>
James Feist6714a252018-09-10 15:26:18 -07009#include <boost/lexical_cast.hpp>
James Feistdc6c55f2018-10-31 12:53:20 -070010#include <cmath>
James Feist6714a252018-09-10 15:26:18 -070011#include <fstream>
12#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070013#include <memory>
14#include <stdexcept>
15#include <string>
16#include <utility>
17#include <variant>
18#include <vector>
James Feist6714a252018-09-10 15:26:18 -070019
20static constexpr bool DEBUG = false;
James Feist6714a252018-09-10 15:26:18 -070021namespace thresholds
22{
James Feistd8705872019-02-08 13:26:09 -080023unsigned int toBusValue(const Level& level)
James Feist6714a252018-09-10 15:26:18 -070024{
25 switch (level)
26 {
27 case (Level::WARNING):
28 {
29 return 0;
30 }
31 case (Level::CRITICAL):
32 {
33 return 1;
34 }
35 default:
36 {
37 return -1;
38 }
39 }
40}
41
James Feistd8705872019-02-08 13:26:09 -080042std::string toBusValue(const Direction& direction)
James Feist6714a252018-09-10 15:26:18 -070043{
44 switch (direction)
45 {
46 case (Direction::LOW):
47 {
48 return "less than";
49 }
50 case (Direction::HIGH):
51 {
52 return "greater than";
53 }
54 default:
55 {
56 return "err";
57 }
58 }
59}
60
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070061bool parseThresholdsFromConfig(
James Feistd8705872019-02-08 13:26:09 -080062 const SensorData& sensorData,
63 std::vector<thresholds::Threshold>& thresholdVector,
64 const std::string* matchLabel)
James Feist6714a252018-09-10 15:26:18 -070065{
James Feistd8705872019-02-08 13:26:09 -080066 for (const auto& item : sensorData)
James Feist6714a252018-09-10 15:26:18 -070067 {
68 if (item.first.find("Thresholds") == std::string::npos)
69 {
70 continue;
71 }
72 if (matchLabel != nullptr)
73 {
74 auto labelFind = item.second.find("Label");
75 if (labelFind == item.second.end())
76 continue;
James Feist3eb82622019-02-08 13:10:22 -080077 if (std::visit(VariantToStringVisitor(), labelFind->second) !=
78 *matchLabel)
James Feist6714a252018-09-10 15:26:18 -070079 continue;
80 }
81 auto directionFind = item.second.find("Direction");
82 auto severityFind = item.second.find("Severity");
83 auto valueFind = item.second.find("Value");
84 if (valueFind == item.second.end() ||
85 severityFind == item.second.end() ||
86 directionFind == item.second.end())
87 {
88 std::cerr << "Malformed threshold in configuration\n";
89 return false;
90 }
91 Level level;
92 Direction direction;
James Feist3eb82622019-02-08 13:10:22 -080093 if (std::visit(VariantToUnsignedIntVisitor(), severityFind->second) ==
94 0)
James Feist6714a252018-09-10 15:26:18 -070095 {
96 level = Level::WARNING;
97 }
98 else
99 {
100 level = Level::CRITICAL;
101 }
James Feist3eb82622019-02-08 13:10:22 -0800102 if (std::visit(VariantToStringVisitor(), directionFind->second) ==
103 "less than")
James Feist6714a252018-09-10 15:26:18 -0700104 {
105 direction = Direction::LOW;
106 }
107 else
108 {
109 direction = Direction::HIGH;
110 }
James Feist13f340b2019-03-07 16:36:11 -0800111 double val = std::visit(VariantToDoubleVisitor(), valueFind->second);
James Feist6714a252018-09-10 15:26:18 -0700112
113 thresholdVector.emplace_back(level, direction, val);
114 }
115 return true;
116}
117
James Feistd8705872019-02-08 13:26:09 -0800118void persistThreshold(const std::string& path, const std::string& baseInterface,
119 const thresholds::Threshold& threshold,
James Feista222ba72019-03-01 15:57:51 -0800120 std::shared_ptr<sdbusplus::asio::connection>& conn,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800121 size_t thresholdCount, const std::string& labelMatch)
James Feist6714a252018-09-10 15:26:18 -0700122{
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500123 for (size_t ii = 0; ii < thresholdCount; ii++)
James Feist6714a252018-09-10 15:26:18 -0700124 {
125 std::string thresholdInterface =
126 baseInterface + ".Thresholds" + std::to_string(ii);
127 conn->async_method_call(
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800128 [&, path, threshold, thresholdInterface, labelMatch](
James Feistd8705872019-02-08 13:26:09 -0800129 const boost::system::error_code& ec,
130 const boost::container::flat_map<std::string, BasicVariantType>&
131 result) {
James Feist6714a252018-09-10 15:26:18 -0700132 if (ec)
133 {
134 return; // threshold not supported
135 }
136
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800137 if (!labelMatch.empty())
138 {
139 auto labelFind = result.find("Label");
140 if (labelFind == result.end())
141 {
142 std::cerr << "No label in threshold configuration\n";
143 return;
144 }
145 std::string label =
146 std::visit(VariantToStringVisitor(), labelFind->second);
147 if (label != labelMatch)
148 {
149 return;
150 }
151 }
152
James Feist6714a252018-09-10 15:26:18 -0700153 auto directionFind = result.find("Direction");
154 auto severityFind = result.find("Severity");
155 auto valueFind = result.find("Value");
156 if (valueFind == result.end() || severityFind == result.end() ||
157 directionFind == result.end())
158 {
159 std::cerr << "Malformed threshold in configuration\n";
160 return;
161 }
James Feist3eb82622019-02-08 13:10:22 -0800162 unsigned int level = std::visit(VariantToUnsignedIntVisitor(),
163 severityFind->second);
James Feist6714a252018-09-10 15:26:18 -0700164
James Feist3eb82622019-02-08 13:10:22 -0800165 std::string dir =
166 std::visit(VariantToStringVisitor(), directionFind->second);
James Feist6714a252018-09-10 15:26:18 -0700167 if ((toBusValue(threshold.level) != level) ||
168 (toBusValue(threshold.direction) != dir))
169 {
170 return; // not the droid we're looking for
171 }
172
James Feist3eb82622019-02-08 13:10:22 -0800173 std::variant<double> value(threshold.value);
James Feist6714a252018-09-10 15:26:18 -0700174 conn->async_method_call(
James Feistd8705872019-02-08 13:26:09 -0800175 [](const boost::system::error_code& ec) {
James Feist6714a252018-09-10 15:26:18 -0700176 if (ec)
177 {
178 std::cerr << "Error setting threshold " << ec
179 << "\n";
180 }
181 },
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700182 entityManagerName, path, "org.freedesktop.DBus.Properties",
183 "Set", thresholdInterface, "Value", value);
James Feist6714a252018-09-10 15:26:18 -0700184 },
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700185 entityManagerName, path, "org.freedesktop.DBus.Properties",
James Feist6714a252018-09-10 15:26:18 -0700186 "GetAll", thresholdInterface);
187 }
188}
189
Jae Hyun Yoo95b8a2d2019-02-25 20:15:09 -0800190void updateThresholds(Sensor* sensor)
191{
192 if (sensor->thresholds.empty())
193 {
194 return;
195 }
196
197 for (const auto& threshold : sensor->thresholds)
198 {
199 std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
200 std::string property;
201 if (threshold.level == thresholds::Level::CRITICAL)
202 {
203 interface = sensor->thresholdInterfaceCritical;
204 if (threshold.direction == thresholds::Direction::HIGH)
205 {
206 property = "CriticalHigh";
207 }
208 else
209 {
210 property = "CriticalLow";
211 }
212 }
213 else if (threshold.level == thresholds::Level::WARNING)
214 {
215 interface = sensor->thresholdInterfaceWarning;
216 if (threshold.direction == thresholds::Direction::HIGH)
217 {
218 property = "WarningHigh";
219 }
220 else
221 {
222 property = "WarningLow";
223 }
224 }
225 else
226 {
227 continue;
228 }
229 if (!interface)
230 {
231 continue;
232 }
233 interface->set_property(property, threshold.value);
234 }
235}
236
Josh Lehan883fb3a2020-02-27 14:41:39 -0800237// Debugging counters
238static int cHiTrue = 0;
239static int cHiFalse = 0;
240static int cHiMidstate = 0;
241static int cLoTrue = 0;
242static int cLoFalse = 0;
243static int cLoMidstate = 0;
244static int cDebugThrottle = 0;
245
James Feist46342ec2019-03-06 14:03:41 -0800246static std::vector<std::pair<Threshold, bool>> checkThresholds(Sensor* sensor,
247 double value)
James Feist251c7822018-09-12 12:54:15 -0700248{
James Feist46342ec2019-03-06 14:03:41 -0800249 std::vector<std::pair<Threshold, bool>> thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700250 if (sensor->thresholds.empty())
251 {
James Feist46342ec2019-03-06 14:03:41 -0800252 return thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700253 }
James Feist46342ec2019-03-06 14:03:41 -0800254
James Feistd8705872019-02-08 13:26:09 -0800255 for (auto& threshold : sensor->thresholds)
James Feist251c7822018-09-12 12:54:15 -0700256 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800257 // Use "Schmitt trigger" logic to avoid threshold trigger spam,
258 // if value is noisy while hovering very close to a threshold.
259 // When a threshold is crossed, indicate true immediately,
260 // but require more distance to be crossed the other direction,
261 // before resetting the indicator back to false.
James Feist46342ec2019-03-06 14:03:41 -0800262 if (threshold.direction == thresholds::Direction::HIGH)
James Feistdc6c55f2018-10-31 12:53:20 -0700263 {
James Feist551087a2019-12-09 11:17:12 -0800264 if (value >= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700265 {
James Feist46342ec2019-03-06 14:03:41 -0800266 thresholdChanges.push_back(std::make_pair(threshold, true));
Josh Lehan883fb3a2020-02-27 14:41:39 -0800267 ++cHiTrue;
268 }
269 else if (value < (threshold.value - sensor->hysteresisTrigger))
270 {
271 thresholdChanges.push_back(std::make_pair(threshold, false));
272 ++cHiFalse;
James Feist251c7822018-09-12 12:54:15 -0700273 }
Patrick Venture66235d42019-10-11 08:31:27 -0700274 else
James Feist251c7822018-09-12 12:54:15 -0700275 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800276 ++cHiMidstate;
James Feist251c7822018-09-12 12:54:15 -0700277 }
278 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800279 else if (threshold.direction == thresholds::Direction::LOW)
James Feist251c7822018-09-12 12:54:15 -0700280 {
James Feist551087a2019-12-09 11:17:12 -0800281 if (value <= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700282 {
James Feist46342ec2019-03-06 14:03:41 -0800283 thresholdChanges.push_back(std::make_pair(threshold, true));
Josh Lehan883fb3a2020-02-27 14:41:39 -0800284 ++cLoTrue;
285 }
286 else if (value > (threshold.value + sensor->hysteresisTrigger))
287 {
288 thresholdChanges.push_back(std::make_pair(threshold, false));
289 ++cLoFalse;
James Feist251c7822018-09-12 12:54:15 -0700290 }
Patrick Venture66235d42019-10-11 08:31:27 -0700291 else
James Feist251c7822018-09-12 12:54:15 -0700292 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800293 ++cLoMidstate;
James Feist251c7822018-09-12 12:54:15 -0700294 }
295 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800296 else
297 {
298 std::cerr << "Error determining threshold direction\n";
299 }
James Feist251c7822018-09-12 12:54:15 -0700300 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800301
302 if constexpr (DEBUG)
303 {
304 // Throttle debug output, so that it does not continuously spam
305 ++cDebugThrottle;
306 if (cDebugThrottle >= 1000)
307 {
308 cDebugThrottle = 0;
309 std::cerr << "checkThresholds: High T=" << cHiTrue
310 << " F=" << cHiFalse << " M=" << cHiMidstate
311 << ", Low T=" << cLoTrue << " F=" << cLoFalse
312 << " M=" << cLoMidstate << "\n";
313 }
314 }
315
James Feist46342ec2019-03-06 14:03:41 -0800316 return thresholdChanges;
317}
318
319bool checkThresholds(Sensor* sensor)
320{
James Feist7b18b1e2019-05-14 13:42:09 -0700321 bool status = true;
James Feist46342ec2019-03-06 14:03:41 -0800322 std::vector<std::pair<Threshold, bool>> changes =
323 checkThresholds(sensor, sensor->value);
324 for (const auto& [threshold, asserted] : changes)
325 {
326 assertThresholds(sensor, threshold.level, threshold.direction,
327 asserted);
328 if (threshold.level == thresholds::Level::CRITICAL && asserted)
329 {
James Feist7b18b1e2019-05-14 13:42:09 -0700330 status = false;
James Feist46342ec2019-03-06 14:03:41 -0800331 }
332 }
333
James Feistdc6c55f2018-10-31 12:53:20 -0700334 return status;
James Feist251c7822018-09-12 12:54:15 -0700335}
336
James Feist46342ec2019-03-06 14:03:41 -0800337void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer)
338{
339
340 std::vector<std::pair<Threshold, bool>> changes =
341 checkThresholds(sensor, sensor->value);
342 for (const auto& [threshold, asserted] : changes)
343 {
344 if (asserted)
345 {
346 thresholdTimer.startTimer(threshold);
347 }
348 else
349 {
350 assertThresholds(sensor, threshold.level, threshold.direction,
351 false);
352 }
353 }
354}
355
James Feistd8705872019-02-08 13:26:09 -0800356void assertThresholds(Sensor* sensor, thresholds::Level level,
James Feist251c7822018-09-12 12:54:15 -0700357 thresholds::Direction direction, bool assert)
358{
359 std::string property;
360 std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
361 if (level == thresholds::Level::WARNING &&
362 direction == thresholds::Direction::HIGH)
363 {
364 property = "WarningAlarmHigh";
365 interface = sensor->thresholdInterfaceWarning;
366 }
367 else if (level == thresholds::Level::WARNING &&
368 direction == thresholds::Direction::LOW)
369 {
370 property = "WarningAlarmLow";
371 interface = sensor->thresholdInterfaceWarning;
372 }
373 else if (level == thresholds::Level::CRITICAL &&
374 direction == thresholds::Direction::HIGH)
375 {
376 property = "CriticalAlarmHigh";
377 interface = sensor->thresholdInterfaceCritical;
378 }
379 else if (level == thresholds::Level::CRITICAL &&
380 direction == thresholds::Direction::LOW)
381 {
382 property = "CriticalAlarmLow";
383 interface = sensor->thresholdInterfaceCritical;
384 }
385 else
386 {
387 std::cerr << "Unknown threshold, level " << level << "direction "
388 << direction << "\n";
389 return;
390 }
391 if (!interface)
392 {
393 std::cout << "trying to set uninitialized interface\n";
394 return;
395 }
396 interface->set_property(property, assert);
397}
398
James Feistd8705872019-02-08 13:26:09 -0800399static constexpr std::array<const char*, 4> attrTypes = {"lcrit", "min", "max",
400 "crit"};
James Feist6714a252018-09-10 15:26:18 -0700401
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{
James Feistb6c0b912019-07-09 12:21:44 -0700407 for (const std::string& type : attrTypes)
James Feist6714a252018-09-10 15:26:18 -0700408 {
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700409 auto attrPath = boost::replace_all_copy(inputPath, "input", type);
410 std::ifstream attrFile(attrPath);
411 if (!attrFile.good())
412 {
James Feist6714a252018-09-10 15:26:18 -0700413 continue;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700414 }
James Feist6714a252018-09-10 15:26:18 -0700415 std::string attr;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700416 std::getline(attrFile, attr);
417 attrFile.close();
James Feist6714a252018-09-10 15:26:18 -0700418
419 Level level;
420 Direction direction;
Jae Hyun Yoo7fa17c42019-09-09 13:20:37 -0700421 double val;
422 try
423 {
424 val = std::stod(attr) / scaleFactor;
425 }
426 catch (const std::invalid_argument&)
427 {
428 return false;
429 }
430
James Feist6714a252018-09-10 15:26:18 -0700431 if (type == "min" || type == "max")
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700432 {
James Feist6714a252018-09-10 15:26:18 -0700433 level = Level::WARNING;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700434 }
James Feist6714a252018-09-10 15:26:18 -0700435 else
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700436 {
James Feist6714a252018-09-10 15:26:18 -0700437 level = Level::CRITICAL;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700438 }
James Feist6714a252018-09-10 15:26:18 -0700439 if (type == "min" || type == "lcrit")
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700440 {
James Feist6714a252018-09-10 15:26:18 -0700441 direction = Direction::LOW;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700442 }
James Feist6714a252018-09-10 15:26:18 -0700443 else
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700444 {
James Feist6714a252018-09-10 15:26:18 -0700445 direction = Direction::HIGH;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700446 }
James Feist6714a252018-09-10 15:26:18 -0700447
Vijay Khemka86dea2b2019-06-06 11:14:37 -0700448 if (type == "crit")
449 {
450 val += offset;
451 }
452
James Feist6714a252018-09-10 15:26:18 -0700453 if (DEBUG)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700454 {
455 std::cout << "Threshold: " << attrPath << ": " << val << "\n";
456 }
James Feist6714a252018-09-10 15:26:18 -0700457
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700458 thresholdVector.emplace_back(level, direction, val);
James Feist6714a252018-09-10 15:26:18 -0700459 }
Jae Hyun Yoo7fa17c42019-09-09 13:20:37 -0700460 // no thresholds is allowed, not an error so return true.
James Feist6714a252018-09-10 15:26:18 -0700461 return true;
462}
463
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700464bool hasCriticalInterface(
James Feistd8705872019-02-08 13:26:09 -0800465 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700466{
James Feistd8705872019-02-08 13:26:09 -0800467 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700468 {
469 if (threshold.level == Level::CRITICAL)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700470 {
James Feist6714a252018-09-10 15:26:18 -0700471 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700472 }
James Feist6714a252018-09-10 15:26:18 -0700473 }
474 return false;
475}
476
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700477bool hasWarningInterface(
James Feistd8705872019-02-08 13:26:09 -0800478 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700479{
James Feistd8705872019-02-08 13:26:09 -0800480 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700481 {
482 if (threshold.level == Level::WARNING)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700483 {
James Feist6714a252018-09-10 15:26:18 -0700484 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700485 }
James Feist6714a252018-09-10 15:26:18 -0700486 }
487 return false;
488}
489} // namespace thresholds