blob: 0b33829faa76b5144e2706a323e596543380f378 [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,
121 size_t thresholdCount)
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(
128 [&, path, threshold, thresholdInterface](
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
137 auto directionFind = result.find("Direction");
138 auto severityFind = result.find("Severity");
139 auto valueFind = result.find("Value");
140 if (valueFind == result.end() || severityFind == result.end() ||
141 directionFind == result.end())
142 {
143 std::cerr << "Malformed threshold in configuration\n";
144 return;
145 }
James Feist3eb82622019-02-08 13:10:22 -0800146 unsigned int level = std::visit(VariantToUnsignedIntVisitor(),
147 severityFind->second);
James Feist6714a252018-09-10 15:26:18 -0700148
James Feist3eb82622019-02-08 13:10:22 -0800149 std::string dir =
150 std::visit(VariantToStringVisitor(), directionFind->second);
James Feist6714a252018-09-10 15:26:18 -0700151 if ((toBusValue(threshold.level) != level) ||
152 (toBusValue(threshold.direction) != dir))
153 {
154 return; // not the droid we're looking for
155 }
156
James Feist3eb82622019-02-08 13:10:22 -0800157 std::variant<double> value(threshold.value);
James Feist6714a252018-09-10 15:26:18 -0700158 conn->async_method_call(
James Feistd8705872019-02-08 13:26:09 -0800159 [](const boost::system::error_code& ec) {
James Feist6714a252018-09-10 15:26:18 -0700160 if (ec)
161 {
162 std::cerr << "Error setting threshold " << ec
163 << "\n";
164 }
165 },
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700166 entityManagerName, path, "org.freedesktop.DBus.Properties",
167 "Set", thresholdInterface, "Value", value);
James Feist6714a252018-09-10 15:26:18 -0700168 },
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700169 entityManagerName, path, "org.freedesktop.DBus.Properties",
James Feist6714a252018-09-10 15:26:18 -0700170 "GetAll", thresholdInterface);
171 }
172}
173
Jae Hyun Yoo95b8a2d2019-02-25 20:15:09 -0800174void updateThresholds(Sensor* sensor)
175{
176 if (sensor->thresholds.empty())
177 {
178 return;
179 }
180
181 for (const auto& threshold : sensor->thresholds)
182 {
183 std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
184 std::string property;
185 if (threshold.level == thresholds::Level::CRITICAL)
186 {
187 interface = sensor->thresholdInterfaceCritical;
188 if (threshold.direction == thresholds::Direction::HIGH)
189 {
190 property = "CriticalHigh";
191 }
192 else
193 {
194 property = "CriticalLow";
195 }
196 }
197 else if (threshold.level == thresholds::Level::WARNING)
198 {
199 interface = sensor->thresholdInterfaceWarning;
200 if (threshold.direction == thresholds::Direction::HIGH)
201 {
202 property = "WarningHigh";
203 }
204 else
205 {
206 property = "WarningLow";
207 }
208 }
209 else
210 {
211 continue;
212 }
213 if (!interface)
214 {
215 continue;
216 }
217 interface->set_property(property, threshold.value);
218 }
219}
220
Josh Lehan883fb3a2020-02-27 14:41:39 -0800221// Debugging counters
222static int cHiTrue = 0;
223static int cHiFalse = 0;
224static int cHiMidstate = 0;
225static int cLoTrue = 0;
226static int cLoFalse = 0;
227static int cLoMidstate = 0;
228static int cDebugThrottle = 0;
229
James Feist46342ec2019-03-06 14:03:41 -0800230static std::vector<std::pair<Threshold, bool>> checkThresholds(Sensor* sensor,
231 double value)
James Feist251c7822018-09-12 12:54:15 -0700232{
James Feist46342ec2019-03-06 14:03:41 -0800233 std::vector<std::pair<Threshold, bool>> thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700234 if (sensor->thresholds.empty())
235 {
James Feist46342ec2019-03-06 14:03:41 -0800236 return thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700237 }
James Feist46342ec2019-03-06 14:03:41 -0800238
James Feistd8705872019-02-08 13:26:09 -0800239 for (auto& threshold : sensor->thresholds)
James Feist251c7822018-09-12 12:54:15 -0700240 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800241 // Use "Schmitt trigger" logic to avoid threshold trigger spam,
242 // if value is noisy while hovering very close to a threshold.
243 // When a threshold is crossed, indicate true immediately,
244 // but require more distance to be crossed the other direction,
245 // before resetting the indicator back to false.
James Feist46342ec2019-03-06 14:03:41 -0800246 if (threshold.direction == thresholds::Direction::HIGH)
James Feistdc6c55f2018-10-31 12:53:20 -0700247 {
James Feist551087a2019-12-09 11:17:12 -0800248 if (value >= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700249 {
James Feist46342ec2019-03-06 14:03:41 -0800250 thresholdChanges.push_back(std::make_pair(threshold, true));
Josh Lehan883fb3a2020-02-27 14:41:39 -0800251 ++cHiTrue;
252 }
253 else if (value < (threshold.value - sensor->hysteresisTrigger))
254 {
255 thresholdChanges.push_back(std::make_pair(threshold, false));
256 ++cHiFalse;
James Feist251c7822018-09-12 12:54:15 -0700257 }
Patrick Venture66235d42019-10-11 08:31:27 -0700258 else
James Feist251c7822018-09-12 12:54:15 -0700259 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800260 ++cHiMidstate;
James Feist251c7822018-09-12 12:54:15 -0700261 }
262 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800263 else if (threshold.direction == thresholds::Direction::LOW)
James Feist251c7822018-09-12 12:54:15 -0700264 {
James Feist551087a2019-12-09 11:17:12 -0800265 if (value <= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700266 {
James Feist46342ec2019-03-06 14:03:41 -0800267 thresholdChanges.push_back(std::make_pair(threshold, true));
Josh Lehan883fb3a2020-02-27 14:41:39 -0800268 ++cLoTrue;
269 }
270 else if (value > (threshold.value + sensor->hysteresisTrigger))
271 {
272 thresholdChanges.push_back(std::make_pair(threshold, false));
273 ++cLoFalse;
James Feist251c7822018-09-12 12:54:15 -0700274 }
Patrick Venture66235d42019-10-11 08:31:27 -0700275 else
James Feist251c7822018-09-12 12:54:15 -0700276 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800277 ++cLoMidstate;
James Feist251c7822018-09-12 12:54:15 -0700278 }
279 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800280 else
281 {
282 std::cerr << "Error determining threshold direction\n";
283 }
James Feist251c7822018-09-12 12:54:15 -0700284 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800285
286 if constexpr (DEBUG)
287 {
288 // Throttle debug output, so that it does not continuously spam
289 ++cDebugThrottle;
290 if (cDebugThrottle >= 1000)
291 {
292 cDebugThrottle = 0;
293 std::cerr << "checkThresholds: High T=" << cHiTrue
294 << " F=" << cHiFalse << " M=" << cHiMidstate
295 << ", Low T=" << cLoTrue << " F=" << cLoFalse
296 << " M=" << cLoMidstate << "\n";
297 }
298 }
299
James Feist46342ec2019-03-06 14:03:41 -0800300 return thresholdChanges;
301}
302
303bool checkThresholds(Sensor* sensor)
304{
James Feist7b18b1e2019-05-14 13:42:09 -0700305 bool status = true;
James Feist46342ec2019-03-06 14:03:41 -0800306 std::vector<std::pair<Threshold, bool>> changes =
307 checkThresholds(sensor, sensor->value);
308 for (const auto& [threshold, asserted] : changes)
309 {
310 assertThresholds(sensor, threshold.level, threshold.direction,
311 asserted);
312 if (threshold.level == thresholds::Level::CRITICAL && asserted)
313 {
James Feist7b18b1e2019-05-14 13:42:09 -0700314 status = false;
James Feist46342ec2019-03-06 14:03:41 -0800315 }
316 }
317
James Feistdc6c55f2018-10-31 12:53:20 -0700318 return status;
James Feist251c7822018-09-12 12:54:15 -0700319}
320
James Feist46342ec2019-03-06 14:03:41 -0800321void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer)
322{
323
324 std::vector<std::pair<Threshold, bool>> changes =
325 checkThresholds(sensor, sensor->value);
326 for (const auto& [threshold, asserted] : changes)
327 {
328 if (asserted)
329 {
330 thresholdTimer.startTimer(threshold);
331 }
332 else
333 {
334 assertThresholds(sensor, threshold.level, threshold.direction,
335 false);
336 }
337 }
338}
339
James Feistd8705872019-02-08 13:26:09 -0800340void assertThresholds(Sensor* sensor, thresholds::Level level,
James Feist251c7822018-09-12 12:54:15 -0700341 thresholds::Direction direction, bool assert)
342{
343 std::string property;
344 std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
345 if (level == thresholds::Level::WARNING &&
346 direction == thresholds::Direction::HIGH)
347 {
348 property = "WarningAlarmHigh";
349 interface = sensor->thresholdInterfaceWarning;
350 }
351 else if (level == thresholds::Level::WARNING &&
352 direction == thresholds::Direction::LOW)
353 {
354 property = "WarningAlarmLow";
355 interface = sensor->thresholdInterfaceWarning;
356 }
357 else if (level == thresholds::Level::CRITICAL &&
358 direction == thresholds::Direction::HIGH)
359 {
360 property = "CriticalAlarmHigh";
361 interface = sensor->thresholdInterfaceCritical;
362 }
363 else if (level == thresholds::Level::CRITICAL &&
364 direction == thresholds::Direction::LOW)
365 {
366 property = "CriticalAlarmLow";
367 interface = sensor->thresholdInterfaceCritical;
368 }
369 else
370 {
371 std::cerr << "Unknown threshold, level " << level << "direction "
372 << direction << "\n";
373 return;
374 }
375 if (!interface)
376 {
377 std::cout << "trying to set uninitialized interface\n";
378 return;
379 }
380 interface->set_property(property, assert);
381}
382
James Feistd8705872019-02-08 13:26:09 -0800383static constexpr std::array<const char*, 4> attrTypes = {"lcrit", "min", "max",
384 "crit"};
James Feist6714a252018-09-10 15:26:18 -0700385
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700386bool parseThresholdsFromAttr(
James Feistd8705872019-02-08 13:26:09 -0800387 std::vector<thresholds::Threshold>& thresholdVector,
Vijay Khemka86dea2b2019-06-06 11:14:37 -0700388 const std::string& inputPath, const double& scaleFactor,
389 const double& offset)
James Feist6714a252018-09-10 15:26:18 -0700390{
James Feistb6c0b912019-07-09 12:21:44 -0700391 for (const std::string& type : attrTypes)
James Feist6714a252018-09-10 15:26:18 -0700392 {
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700393 auto attrPath = boost::replace_all_copy(inputPath, "input", type);
394 std::ifstream attrFile(attrPath);
395 if (!attrFile.good())
396 {
James Feist6714a252018-09-10 15:26:18 -0700397 continue;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700398 }
James Feist6714a252018-09-10 15:26:18 -0700399 std::string attr;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700400 std::getline(attrFile, attr);
401 attrFile.close();
James Feist6714a252018-09-10 15:26:18 -0700402
403 Level level;
404 Direction direction;
Jae Hyun Yoo7fa17c42019-09-09 13:20:37 -0700405 double val;
406 try
407 {
408 val = std::stod(attr) / scaleFactor;
409 }
410 catch (const std::invalid_argument&)
411 {
412 return false;
413 }
414
James Feist6714a252018-09-10 15:26:18 -0700415 if (type == "min" || type == "max")
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700416 {
James Feist6714a252018-09-10 15:26:18 -0700417 level = Level::WARNING;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700418 }
James Feist6714a252018-09-10 15:26:18 -0700419 else
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700420 {
James Feist6714a252018-09-10 15:26:18 -0700421 level = Level::CRITICAL;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700422 }
James Feist6714a252018-09-10 15:26:18 -0700423 if (type == "min" || type == "lcrit")
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700424 {
James Feist6714a252018-09-10 15:26:18 -0700425 direction = Direction::LOW;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700426 }
James Feist6714a252018-09-10 15:26:18 -0700427 else
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700428 {
James Feist6714a252018-09-10 15:26:18 -0700429 direction = Direction::HIGH;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700430 }
James Feist6714a252018-09-10 15:26:18 -0700431
Vijay Khemka86dea2b2019-06-06 11:14:37 -0700432 if (type == "crit")
433 {
434 val += offset;
435 }
436
James Feist6714a252018-09-10 15:26:18 -0700437 if (DEBUG)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700438 {
439 std::cout << "Threshold: " << attrPath << ": " << val << "\n";
440 }
James Feist6714a252018-09-10 15:26:18 -0700441
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700442 thresholdVector.emplace_back(level, direction, val);
James Feist6714a252018-09-10 15:26:18 -0700443 }
Jae Hyun Yoo7fa17c42019-09-09 13:20:37 -0700444 // no thresholds is allowed, not an error so return true.
James Feist6714a252018-09-10 15:26:18 -0700445 return true;
446}
447
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700448bool hasCriticalInterface(
James Feistd8705872019-02-08 13:26:09 -0800449 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700450{
James Feistd8705872019-02-08 13:26:09 -0800451 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700452 {
453 if (threshold.level == Level::CRITICAL)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700454 {
James Feist6714a252018-09-10 15:26:18 -0700455 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700456 }
James Feist6714a252018-09-10 15:26:18 -0700457 }
458 return false;
459}
460
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700461bool hasWarningInterface(
James Feistd8705872019-02-08 13:26:09 -0800462 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700463{
James Feistd8705872019-02-08 13:26:09 -0800464 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700465 {
466 if (threshold.level == Level::WARNING)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700467 {
James Feist6714a252018-09-10 15:26:18 -0700468 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700469 }
James Feist6714a252018-09-10 15:26:18 -0700470 }
471 return false;
472}
473} // namespace thresholds