1 #include "Thresholds.hpp" 2 3 #include "VariantVisitors.hpp" 4 #include "sensor.hpp" 5 6 #include <array> 7 #include <boost/algorithm/string/replace.hpp> 8 #include <boost/container/flat_map.hpp> 9 #include <boost/lexical_cast.hpp> 10 #include <cmath> 11 #include <fstream> 12 #include <iostream> 13 #include <memory> 14 #include <stdexcept> 15 #include <string> 16 #include <utility> 17 #include <variant> 18 #include <vector> 19 20 static constexpr bool DEBUG = false; 21 namespace thresholds 22 { 23 unsigned int toBusValue(const Level& level) 24 { 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 42 std::string toBusValue(const Direction& direction) 43 { 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 61 bool parseThresholdsFromConfig( 62 const SensorData& sensorData, 63 std::vector<thresholds::Threshold>& thresholdVector, 64 const std::string* matchLabel) 65 { 66 for (const auto& item : sensorData) 67 { 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; 77 if (std::visit(VariantToStringVisitor(), labelFind->second) != 78 *matchLabel) 79 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; 93 if (std::visit(VariantToUnsignedIntVisitor(), severityFind->second) == 94 0) 95 { 96 level = Level::WARNING; 97 } 98 else 99 { 100 level = Level::CRITICAL; 101 } 102 if (std::visit(VariantToStringVisitor(), directionFind->second) == 103 "less than") 104 { 105 direction = Direction::LOW; 106 } 107 else 108 { 109 direction = Direction::HIGH; 110 } 111 double val = std::visit(VariantToDoubleVisitor(), valueFind->second); 112 113 thresholdVector.emplace_back(level, direction, val); 114 } 115 return true; 116 } 117 118 void persistThreshold(const std::string& path, const std::string& baseInterface, 119 const thresholds::Threshold& threshold, 120 std::shared_ptr<sdbusplus::asio::connection>& conn, 121 size_t thresholdCount) 122 { 123 for (size_t ii = 0; ii < thresholdCount; ii++) 124 { 125 std::string thresholdInterface = 126 baseInterface + ".Thresholds" + std::to_string(ii); 127 conn->async_method_call( 128 [&, path, threshold, thresholdInterface]( 129 const boost::system::error_code& ec, 130 const boost::container::flat_map<std::string, BasicVariantType>& 131 result) { 132 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 } 146 unsigned int level = std::visit(VariantToUnsignedIntVisitor(), 147 severityFind->second); 148 149 std::string dir = 150 std::visit(VariantToStringVisitor(), directionFind->second); 151 if ((toBusValue(threshold.level) != level) || 152 (toBusValue(threshold.direction) != dir)) 153 { 154 return; // not the droid we're looking for 155 } 156 157 std::variant<double> value(threshold.value); 158 conn->async_method_call( 159 [](const boost::system::error_code& ec) { 160 if (ec) 161 { 162 std::cerr << "Error setting threshold " << ec 163 << "\n"; 164 } 165 }, 166 entityManagerName, path, "org.freedesktop.DBus.Properties", 167 "Set", thresholdInterface, "Value", value); 168 }, 169 entityManagerName, path, "org.freedesktop.DBus.Properties", 170 "GetAll", thresholdInterface); 171 } 172 } 173 174 void 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 221 static std::vector<std::pair<Threshold, bool>> checkThresholds(Sensor* sensor, 222 double value) 223 { 224 std::vector<std::pair<Threshold, bool>> thresholdChanges; 225 if (sensor->thresholds.empty()) 226 { 227 return thresholdChanges; 228 } 229 230 for (auto& threshold : sensor->thresholds) 231 { 232 if (threshold.direction == thresholds::Direction::HIGH) 233 { 234 if (value >= threshold.value) 235 { 236 thresholdChanges.push_back(std::make_pair(threshold, true)); 237 } 238 else 239 { 240 thresholdChanges.push_back(std::make_pair(threshold, false)); 241 } 242 } 243 else 244 { 245 if (value <= threshold.value) 246 { 247 thresholdChanges.push_back(std::make_pair(threshold, true)); 248 } 249 else 250 { 251 thresholdChanges.push_back(std::make_pair(threshold, false)); 252 } 253 } 254 } 255 return thresholdChanges; 256 } 257 258 bool checkThresholds(Sensor* sensor) 259 { 260 bool status = true; 261 std::vector<std::pair<Threshold, bool>> changes = 262 checkThresholds(sensor, sensor->value); 263 for (const auto& [threshold, asserted] : changes) 264 { 265 assertThresholds(sensor, threshold.level, threshold.direction, 266 asserted); 267 if (threshold.level == thresholds::Level::CRITICAL && asserted) 268 { 269 status = false; 270 } 271 } 272 273 return status; 274 } 275 276 void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer) 277 { 278 279 std::vector<std::pair<Threshold, bool>> changes = 280 checkThresholds(sensor, sensor->value); 281 for (const auto& [threshold, asserted] : changes) 282 { 283 if (asserted) 284 { 285 thresholdTimer.startTimer(threshold); 286 } 287 else 288 { 289 assertThresholds(sensor, threshold.level, threshold.direction, 290 false); 291 } 292 } 293 } 294 295 void assertThresholds(Sensor* sensor, thresholds::Level level, 296 thresholds::Direction direction, bool assert) 297 { 298 std::string property; 299 std::shared_ptr<sdbusplus::asio::dbus_interface> interface; 300 if (level == thresholds::Level::WARNING && 301 direction == thresholds::Direction::HIGH) 302 { 303 property = "WarningAlarmHigh"; 304 interface = sensor->thresholdInterfaceWarning; 305 } 306 else if (level == thresholds::Level::WARNING && 307 direction == thresholds::Direction::LOW) 308 { 309 property = "WarningAlarmLow"; 310 interface = sensor->thresholdInterfaceWarning; 311 } 312 else if (level == thresholds::Level::CRITICAL && 313 direction == thresholds::Direction::HIGH) 314 { 315 property = "CriticalAlarmHigh"; 316 interface = sensor->thresholdInterfaceCritical; 317 } 318 else if (level == thresholds::Level::CRITICAL && 319 direction == thresholds::Direction::LOW) 320 { 321 property = "CriticalAlarmLow"; 322 interface = sensor->thresholdInterfaceCritical; 323 } 324 else 325 { 326 std::cerr << "Unknown threshold, level " << level << "direction " 327 << direction << "\n"; 328 return; 329 } 330 if (!interface) 331 { 332 std::cout << "trying to set uninitialized interface\n"; 333 return; 334 } 335 interface->set_property(property, assert); 336 } 337 338 static constexpr std::array<const char*, 4> attrTypes = {"lcrit", "min", "max", 339 "crit"}; 340 341 bool parseThresholdsFromAttr( 342 std::vector<thresholds::Threshold>& thresholdVector, 343 const std::string& inputPath, const double& scaleFactor, 344 const double& offset) 345 { 346 for (const std::string& type : attrTypes) 347 { 348 auto attrPath = boost::replace_all_copy(inputPath, "input", type); 349 std::ifstream attrFile(attrPath); 350 if (!attrFile.good()) 351 { 352 continue; 353 } 354 std::string attr; 355 std::getline(attrFile, attr); 356 attrFile.close(); 357 358 Level level; 359 Direction direction; 360 double val; 361 try 362 { 363 val = std::stod(attr) / scaleFactor; 364 } 365 catch (const std::invalid_argument&) 366 { 367 return false; 368 } 369 370 if (type == "min" || type == "max") 371 { 372 level = Level::WARNING; 373 } 374 else 375 { 376 level = Level::CRITICAL; 377 } 378 if (type == "min" || type == "lcrit") 379 { 380 direction = Direction::LOW; 381 } 382 else 383 { 384 direction = Direction::HIGH; 385 } 386 387 if (type == "crit") 388 { 389 val += offset; 390 } 391 392 if (DEBUG) 393 { 394 std::cout << "Threshold: " << attrPath << ": " << val << "\n"; 395 } 396 397 thresholdVector.emplace_back(level, direction, val); 398 } 399 // no thresholds is allowed, not an error so return true. 400 return true; 401 } 402 403 bool hasCriticalInterface( 404 const std::vector<thresholds::Threshold>& thresholdVector) 405 { 406 for (auto& threshold : thresholdVector) 407 { 408 if (threshold.level == Level::CRITICAL) 409 { 410 return true; 411 } 412 } 413 return false; 414 } 415 416 bool hasWarningInterface( 417 const std::vector<thresholds::Threshold>& thresholdVector) 418 { 419 for (auto& threshold : thresholdVector) 420 { 421 if (threshold.level == Level::WARNING) 422 { 423 return true; 424 } 425 } 426 return false; 427 } 428 } // namespace thresholds 429