1 #include <Thresholds.hpp> 2 #include <VariantVisitors.hpp> 3 #include <boost/algorithm/string/replace.hpp> 4 #include <boost/lexical_cast.hpp> 5 #include <cmath> 6 #include <fstream> 7 #include <iostream> 8 #include <sensor.hpp> 9 10 static constexpr bool DEBUG = false; 11 namespace thresholds 12 { 13 unsigned int toBusValue(const Level& level) 14 { 15 switch (level) 16 { 17 case (Level::WARNING): 18 { 19 return 0; 20 } 21 case (Level::CRITICAL): 22 { 23 return 1; 24 } 25 default: 26 { 27 return -1; 28 } 29 } 30 } 31 32 std::string toBusValue(const Direction& direction) 33 { 34 switch (direction) 35 { 36 case (Direction::LOW): 37 { 38 return "less than"; 39 } 40 case (Direction::HIGH): 41 { 42 return "greater than"; 43 } 44 default: 45 { 46 return "err"; 47 } 48 } 49 } 50 51 bool parseThresholdsFromConfig( 52 const SensorData& sensorData, 53 std::vector<thresholds::Threshold>& thresholdVector, 54 const std::string* matchLabel) 55 { 56 for (const auto& item : sensorData) 57 { 58 if (item.first.find("Thresholds") == std::string::npos) 59 { 60 continue; 61 } 62 if (matchLabel != nullptr) 63 { 64 auto labelFind = item.second.find("Label"); 65 if (labelFind == item.second.end()) 66 continue; 67 if (std::visit(VariantToStringVisitor(), labelFind->second) != 68 *matchLabel) 69 continue; 70 } 71 auto directionFind = item.second.find("Direction"); 72 auto severityFind = item.second.find("Severity"); 73 auto valueFind = item.second.find("Value"); 74 if (valueFind == item.second.end() || 75 severityFind == item.second.end() || 76 directionFind == item.second.end()) 77 { 78 std::cerr << "Malformed threshold in configuration\n"; 79 return false; 80 } 81 Level level; 82 Direction direction; 83 if (std::visit(VariantToUnsignedIntVisitor(), severityFind->second) == 84 0) 85 { 86 level = Level::WARNING; 87 } 88 else 89 { 90 level = Level::CRITICAL; 91 } 92 if (std::visit(VariantToStringVisitor(), directionFind->second) == 93 "less than") 94 { 95 direction = Direction::LOW; 96 } 97 else 98 { 99 direction = Direction::HIGH; 100 } 101 double val = std::visit(VariantToDoubleVisitor(), valueFind->second); 102 103 thresholdVector.emplace_back(level, direction, val); 104 } 105 return true; 106 } 107 108 void persistThreshold(const std::string& path, const std::string& baseInterface, 109 const thresholds::Threshold& threshold, 110 std::shared_ptr<sdbusplus::asio::connection>& conn, 111 size_t thresholdCount) 112 { 113 for (int ii = 0; ii < thresholdCount; ii++) 114 { 115 std::string thresholdInterface = 116 baseInterface + ".Thresholds" + std::to_string(ii); 117 conn->async_method_call( 118 [&, path, threshold, thresholdInterface]( 119 const boost::system::error_code& ec, 120 const boost::container::flat_map<std::string, BasicVariantType>& 121 result) { 122 if (ec) 123 { 124 return; // threshold not supported 125 } 126 127 auto directionFind = result.find("Direction"); 128 auto severityFind = result.find("Severity"); 129 auto valueFind = result.find("Value"); 130 if (valueFind == result.end() || severityFind == result.end() || 131 directionFind == result.end()) 132 { 133 std::cerr << "Malformed threshold in configuration\n"; 134 return; 135 } 136 unsigned int level = std::visit(VariantToUnsignedIntVisitor(), 137 severityFind->second); 138 139 std::string dir = 140 std::visit(VariantToStringVisitor(), directionFind->second); 141 if ((toBusValue(threshold.level) != level) || 142 (toBusValue(threshold.direction) != dir)) 143 { 144 return; // not the droid we're looking for 145 } 146 147 std::variant<double> value(threshold.value); 148 conn->async_method_call( 149 [](const boost::system::error_code& ec) { 150 if (ec) 151 { 152 std::cerr << "Error setting threshold " << ec 153 << "\n"; 154 } 155 }, 156 entityManagerName, path, "org.freedesktop.DBus.Properties", 157 "Set", thresholdInterface, "Value", value); 158 }, 159 entityManagerName, path, "org.freedesktop.DBus.Properties", 160 "GetAll", thresholdInterface); 161 } 162 } 163 164 void updateThresholds(Sensor* sensor) 165 { 166 if (sensor->thresholds.empty()) 167 { 168 return; 169 } 170 171 for (const auto& threshold : sensor->thresholds) 172 { 173 std::shared_ptr<sdbusplus::asio::dbus_interface> interface; 174 std::string property; 175 if (threshold.level == thresholds::Level::CRITICAL) 176 { 177 interface = sensor->thresholdInterfaceCritical; 178 if (threshold.direction == thresholds::Direction::HIGH) 179 { 180 property = "CriticalHigh"; 181 } 182 else 183 { 184 property = "CriticalLow"; 185 } 186 } 187 else if (threshold.level == thresholds::Level::WARNING) 188 { 189 interface = sensor->thresholdInterfaceWarning; 190 if (threshold.direction == thresholds::Direction::HIGH) 191 { 192 property = "WarningHigh"; 193 } 194 else 195 { 196 property = "WarningLow"; 197 } 198 } 199 else 200 { 201 continue; 202 } 203 if (!interface) 204 { 205 continue; 206 } 207 interface->set_property(property, threshold.value); 208 } 209 } 210 211 static std::vector<std::pair<Threshold, bool>> checkThresholds(Sensor* sensor, 212 double value) 213 { 214 std::vector<std::pair<Threshold, bool>> thresholdChanges; 215 if (sensor->thresholds.empty()) 216 { 217 return thresholdChanges; 218 } 219 220 for (auto& threshold : sensor->thresholds) 221 { 222 if (threshold.direction == thresholds::Direction::HIGH) 223 { 224 if (value > threshold.value) 225 { 226 thresholdChanges.push_back(std::make_pair(threshold, true)); 227 } 228 else if (value <= threshold.value) 229 { 230 thresholdChanges.push_back(std::make_pair(threshold, false)); 231 } 232 } 233 else 234 { 235 if (value < threshold.value) 236 { 237 thresholdChanges.push_back(std::make_pair(threshold, true)); 238 } 239 else if (value >= threshold.value) 240 { 241 thresholdChanges.push_back(std::make_pair(threshold, false)); 242 } 243 } 244 } 245 return thresholdChanges; 246 } 247 248 bool checkThresholds(Sensor* sensor) 249 { 250 bool status = true; 251 std::vector<std::pair<Threshold, bool>> changes = 252 checkThresholds(sensor, sensor->value); 253 for (const auto& [threshold, asserted] : changes) 254 { 255 assertThresholds(sensor, threshold.level, threshold.direction, 256 asserted); 257 if (threshold.level == thresholds::Level::CRITICAL && asserted) 258 { 259 status = false; 260 } 261 } 262 263 return status; 264 } 265 266 void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer) 267 { 268 269 std::vector<std::pair<Threshold, bool>> changes = 270 checkThresholds(sensor, sensor->value); 271 for (const auto& [threshold, asserted] : changes) 272 { 273 if (asserted) 274 { 275 thresholdTimer.startTimer(threshold); 276 } 277 else 278 { 279 assertThresholds(sensor, threshold.level, threshold.direction, 280 false); 281 } 282 } 283 } 284 285 void assertThresholds(Sensor* sensor, thresholds::Level level, 286 thresholds::Direction direction, bool assert) 287 { 288 std::string property; 289 std::shared_ptr<sdbusplus::asio::dbus_interface> interface; 290 if (level == thresholds::Level::WARNING && 291 direction == thresholds::Direction::HIGH) 292 { 293 property = "WarningAlarmHigh"; 294 interface = sensor->thresholdInterfaceWarning; 295 } 296 else if (level == thresholds::Level::WARNING && 297 direction == thresholds::Direction::LOW) 298 { 299 property = "WarningAlarmLow"; 300 interface = sensor->thresholdInterfaceWarning; 301 } 302 else if (level == thresholds::Level::CRITICAL && 303 direction == thresholds::Direction::HIGH) 304 { 305 property = "CriticalAlarmHigh"; 306 interface = sensor->thresholdInterfaceCritical; 307 } 308 else if (level == thresholds::Level::CRITICAL && 309 direction == thresholds::Direction::LOW) 310 { 311 property = "CriticalAlarmLow"; 312 interface = sensor->thresholdInterfaceCritical; 313 } 314 else 315 { 316 std::cerr << "Unknown threshold, level " << level << "direction " 317 << direction << "\n"; 318 return; 319 } 320 if (!interface) 321 { 322 std::cout << "trying to set uninitialized interface\n"; 323 return; 324 } 325 interface->set_property(property, assert); 326 } 327 328 static constexpr std::array<const char*, 4> attrTypes = {"lcrit", "min", "max", 329 "crit"}; 330 331 bool parseThresholdsFromAttr( 332 std::vector<thresholds::Threshold>& thresholdVector, 333 const std::string& inputPath, const double& scaleFactor, 334 const double& offset) 335 { 336 for (auto& type : attrTypes) 337 { 338 auto attrPath = boost::replace_all_copy(inputPath, "input", type); 339 std::ifstream attrFile(attrPath); 340 if (!attrFile.good()) 341 { 342 continue; 343 } 344 std::string attr; 345 std::getline(attrFile, attr); 346 attrFile.close(); 347 348 Level level; 349 Direction direction; 350 double val = std::stod(attr) / scaleFactor; 351 if (type == "min" || type == "max") 352 { 353 level = Level::WARNING; 354 } 355 else 356 { 357 level = Level::CRITICAL; 358 } 359 if (type == "min" || type == "lcrit") 360 { 361 direction = Direction::LOW; 362 } 363 else 364 { 365 direction = Direction::HIGH; 366 } 367 368 if (type == "crit") 369 { 370 val += offset; 371 } 372 373 if (DEBUG) 374 { 375 std::cout << "Threshold: " << attrPath << ": " << val << "\n"; 376 } 377 378 thresholdVector.emplace_back(level, direction, val); 379 } 380 // no thresholds is allowed, not an error so return true always 381 return true; 382 } 383 384 bool hasCriticalInterface( 385 const std::vector<thresholds::Threshold>& thresholdVector) 386 { 387 for (auto& threshold : thresholdVector) 388 { 389 if (threshold.level == Level::CRITICAL) 390 { 391 return true; 392 } 393 } 394 return false; 395 } 396 397 bool hasWarningInterface( 398 const std::vector<thresholds::Threshold>& thresholdVector) 399 { 400 for (auto& threshold : thresholdVector) 401 { 402 if (threshold.level == Level::WARNING) 403 { 404 return true; 405 } 406 } 407 return false; 408 } 409 } // namespace thresholds 410