| #include <Thresholds.hpp> |
| #include <VariantVisitors.hpp> |
| #include <boost/algorithm/string/replace.hpp> |
| #include <boost/lexical_cast.hpp> |
| #include <fstream> |
| #include <iostream> |
| #include <sensor.hpp> |
| |
| static constexpr bool DEBUG = false; |
| constexpr size_t MAX_THRESHOLDS = 4; |
| |
| namespace variant_ns = sdbusplus::message::variant_ns; |
| namespace thresholds |
| { |
| unsigned int toBusValue(const Level &level) |
| { |
| switch (level) |
| { |
| case (Level::WARNING): |
| { |
| return 0; |
| } |
| case (Level::CRITICAL): |
| { |
| return 1; |
| } |
| default: |
| { |
| return -1; |
| } |
| } |
| } |
| |
| std::string toBusValue(const Direction &direction) |
| { |
| switch (direction) |
| { |
| case (Direction::LOW): |
| { |
| return "less than"; |
| } |
| case (Direction::HIGH): |
| { |
| return "greater than"; |
| } |
| default: |
| { |
| return "err"; |
| } |
| } |
| } |
| |
| bool ParseThresholdsFromConfig( |
| const SensorData &sensorData, |
| std::vector<thresholds::Threshold> &thresholdVector, |
| const std::string *matchLabel) |
| { |
| for (const auto &item : sensorData) |
| { |
| if (item.first.find("Thresholds") == std::string::npos) |
| { |
| continue; |
| } |
| if (matchLabel != nullptr) |
| { |
| auto labelFind = item.second.find("Label"); |
| if (labelFind == item.second.end()) |
| continue; |
| if (variant_ns::visit(VariantToStringVisitor(), |
| labelFind->second) != *matchLabel) |
| continue; |
| } |
| auto directionFind = item.second.find("Direction"); |
| auto severityFind = item.second.find("Severity"); |
| auto valueFind = item.second.find("Value"); |
| if (valueFind == item.second.end() || |
| severityFind == item.second.end() || |
| directionFind == item.second.end()) |
| { |
| std::cerr << "Malformed threshold in configuration\n"; |
| return false; |
| } |
| Level level; |
| Direction direction; |
| if (variant_ns::visit(VariantToUnsignedIntVisitor(), |
| severityFind->second) == 0) |
| { |
| level = Level::WARNING; |
| } |
| else |
| { |
| level = Level::CRITICAL; |
| } |
| if (variant_ns::visit(VariantToStringVisitor(), |
| directionFind->second) == "less than") |
| { |
| direction = Direction::LOW; |
| } |
| else |
| { |
| direction = Direction::HIGH; |
| } |
| float val = |
| variant_ns::visit(VariantToFloatVisitor(), valueFind->second); |
| |
| thresholdVector.emplace_back(level, direction, val); |
| } |
| return true; |
| } |
| |
| void persistThreshold(const std::string &path, const std::string &baseInterface, |
| const thresholds::Threshold &threshold, |
| std::shared_ptr<sdbusplus::asio::connection> &conn) |
| { |
| for (int ii = 0; ii < MAX_THRESHOLDS; ii++) |
| { |
| std::string thresholdInterface = |
| baseInterface + ".Thresholds" + std::to_string(ii); |
| conn->async_method_call( |
| [&, path, threshold, thresholdInterface]( |
| const boost::system::error_code &ec, |
| const boost::container::flat_map<std::string, BasicVariantType> |
| &result) { |
| if (ec) |
| { |
| return; // threshold not supported |
| } |
| |
| auto directionFind = result.find("Direction"); |
| auto severityFind = result.find("Severity"); |
| auto valueFind = result.find("Value"); |
| if (valueFind == result.end() || severityFind == result.end() || |
| directionFind == result.end()) |
| { |
| std::cerr << "Malformed threshold in configuration\n"; |
| return; |
| } |
| unsigned int level = variant_ns::visit( |
| VariantToUnsignedIntVisitor(), severityFind->second); |
| |
| std::string dir = variant_ns::visit(VariantToStringVisitor(), |
| directionFind->second); |
| if ((toBusValue(threshold.level) != level) || |
| (toBusValue(threshold.direction) != dir)) |
| { |
| return; // not the droid we're looking for |
| } |
| |
| sdbusplus::message::variant<double> value(threshold.value); |
| conn->async_method_call( |
| [](const boost::system::error_code &ec) { |
| if (ec) |
| { |
| std::cerr << "Error setting threshold " << ec |
| << "\n"; |
| } |
| }, |
| ENTITY_MANAGER_NAME, path, |
| "org.freedesktop.DBus.Properties", "Set", |
| thresholdInterface, "Value", value); |
| }, |
| ENTITY_MANAGER_NAME, path, "org.freedesktop.DBus.Properties", |
| "GetAll", thresholdInterface); |
| } |
| } |
| |
| void checkThresholds(Sensor *sensor) |
| { |
| |
| if (sensor->thresholds.empty()) |
| { |
| return; |
| } |
| for (auto &threshold : sensor->thresholds) |
| { |
| if (threshold.direction == thresholds::Direction::HIGH) |
| { |
| if (sensor->value > threshold.value && !threshold.asserted) |
| { |
| assertThresholds(sensor, threshold.level, threshold.direction, |
| true); |
| threshold.asserted = true; |
| } |
| else if (sensor->value <= threshold.value && threshold.asserted) |
| { |
| assertThresholds(sensor, threshold.level, threshold.direction, |
| false); |
| threshold.asserted = false; |
| } |
| } |
| else |
| { |
| if (sensor->value < threshold.value && !threshold.asserted) |
| { |
| assertThresholds(sensor, threshold.level, threshold.direction, |
| true); |
| threshold.asserted = true; |
| } |
| else if (sensor->value >= threshold.value && threshold.asserted) |
| { |
| assertThresholds(sensor, threshold.level, threshold.direction, |
| false); |
| threshold.asserted = false; |
| } |
| } |
| } |
| } |
| |
| void assertThresholds(Sensor *sensor, thresholds::Level level, |
| thresholds::Direction direction, bool assert) |
| { |
| std::string property; |
| std::shared_ptr<sdbusplus::asio::dbus_interface> interface; |
| if (level == thresholds::Level::WARNING && |
| direction == thresholds::Direction::HIGH) |
| { |
| property = "WarningAlarmHigh"; |
| interface = sensor->thresholdInterfaceWarning; |
| } |
| else if (level == thresholds::Level::WARNING && |
| direction == thresholds::Direction::LOW) |
| { |
| property = "WarningAlarmLow"; |
| interface = sensor->thresholdInterfaceWarning; |
| } |
| else if (level == thresholds::Level::CRITICAL && |
| direction == thresholds::Direction::HIGH) |
| { |
| property = "CriticalAlarmHigh"; |
| interface = sensor->thresholdInterfaceCritical; |
| } |
| else if (level == thresholds::Level::CRITICAL && |
| direction == thresholds::Direction::LOW) |
| { |
| property = "CriticalAlarmLow"; |
| interface = sensor->thresholdInterfaceCritical; |
| } |
| else |
| { |
| std::cerr << "Unknown threshold, level " << level << "direction " |
| << direction << "\n"; |
| return; |
| } |
| if (!interface) |
| { |
| std::cout << "trying to set uninitialized interface\n"; |
| return; |
| } |
| interface->set_property(property, assert); |
| } |
| |
| static constexpr std::array<const char *, 4> ATTR_TYPES = {"lcrit", "min", |
| "max", "crit"}; |
| |
| bool ParseThresholdsFromAttr( |
| std::vector<thresholds::Threshold> &threshold_vector, |
| const std::string &input_path, const double &scale_factor) |
| { |
| for (auto &type : ATTR_TYPES) |
| { |
| auto attr_path = boost::replace_all_copy(input_path, "input", type); |
| std::ifstream attr_file(attr_path); |
| if (!attr_file.good()) |
| continue; |
| std::string attr; |
| std::getline(attr_file, attr); |
| attr_file.close(); |
| |
| Level level; |
| Direction direction; |
| double val = std::stod(attr) / scale_factor; |
| if (type == "min" || type == "max") |
| level = Level::WARNING; |
| else |
| level = Level::CRITICAL; |
| if (type == "min" || type == "lcrit") |
| direction = Direction::LOW; |
| else |
| direction = Direction::HIGH; |
| |
| if (DEBUG) |
| std::cout << "Threshold: " << attr_path << ": " << val << "\n"; |
| |
| threshold_vector.emplace_back(level, direction, val); |
| } |
| // no thresholds is allowed, not an error so return true always |
| return true; |
| } |
| |
| bool HasCriticalInterface( |
| const std::vector<thresholds::Threshold> &threshold_vector) |
| { |
| for (auto &threshold : threshold_vector) |
| { |
| if (threshold.level == Level::CRITICAL) |
| return true; |
| } |
| return false; |
| } |
| |
| bool HasWarningInterface( |
| const std::vector<thresholds::Threshold> &threshold_vector) |
| { |
| for (auto &threshold : threshold_vector) |
| { |
| if (threshold.level == Level::WARNING) |
| return true; |
| } |
| return false; |
| } |
| } // namespace thresholds |