1 #include <Thresholds.hpp> 2 #include <VariantVisitors.hpp> 3 #include <boost/algorithm/string/replace.hpp> 4 #include <boost/lexical_cast.hpp> 5 #include <fstream> 6 #include <iostream> 7 #include <sensor.hpp> 8 9 static constexpr bool DEBUG = false; 10 constexpr size_t MAX_THRESHOLDS = 4; 11 12 namespace thresholds 13 { 14 unsigned int toBusValue(const Level &level) 15 { 16 switch (level) 17 { 18 case (Level::WARNING): 19 { 20 return 0; 21 } 22 case (Level::CRITICAL): 23 { 24 return 1; 25 } 26 default: 27 { 28 return -1; 29 } 30 } 31 } 32 33 std::string toBusValue(const Direction &direction) 34 { 35 switch (direction) 36 { 37 case (Direction::LOW): 38 { 39 return "less than"; 40 } 41 case (Direction::HIGH): 42 { 43 return "greater than"; 44 } 45 default: 46 { 47 return "err"; 48 } 49 } 50 } 51 52 bool ParseThresholdsFromConfig( 53 const SensorData &sensorData, 54 std::vector<thresholds::Threshold> &thresholdVector, 55 const std::string *matchLabel) 56 { 57 for (const auto &item : sensorData) 58 { 59 if (item.first.find("Thresholds") == std::string::npos) 60 { 61 continue; 62 } 63 if (matchLabel != nullptr) 64 { 65 auto labelFind = item.second.find("Label"); 66 if (labelFind == item.second.end()) 67 continue; 68 if (mapbox::util::apply_visitor(VariantToStringVisitor(), 69 labelFind->second) != *matchLabel) 70 continue; 71 } 72 auto directionFind = item.second.find("Direction"); 73 auto severityFind = item.second.find("Severity"); 74 auto valueFind = item.second.find("Value"); 75 if (valueFind == item.second.end() || 76 severityFind == item.second.end() || 77 directionFind == item.second.end()) 78 { 79 std::cerr << "Malformed threshold in configuration\n"; 80 return false; 81 } 82 Level level; 83 Direction direction; 84 if (mapbox::util::apply_visitor(VariantToUnsignedIntVisitor(), 85 severityFind->second) == 0) 86 { 87 level = Level::WARNING; 88 } 89 else 90 { 91 level = Level::CRITICAL; 92 } 93 if (mapbox::util::apply_visitor(VariantToStringVisitor(), 94 directionFind->second) == "less than") 95 { 96 direction = Direction::LOW; 97 } 98 else 99 { 100 direction = Direction::HIGH; 101 } 102 float val = mapbox::util::apply_visitor(VariantToFloatVisitor(), 103 valueFind->second); 104 105 thresholdVector.emplace_back(level, direction, val); 106 } 107 return true; 108 } 109 110 void persistThreshold(const std::string &path, const std::string &baseInterface, 111 const thresholds::Threshold &threshold, 112 std::shared_ptr<sdbusplus::asio::connection> &conn) 113 { 114 for (int ii = 0; ii < MAX_THRESHOLDS; ii++) 115 { 116 std::string thresholdInterface = 117 baseInterface + ".Thresholds" + std::to_string(ii); 118 conn->async_method_call( 119 [&, path, threshold, thresholdInterface]( 120 const boost::system::error_code &ec, 121 const boost::container::flat_map<std::string, BasicVariantType> 122 &result) { 123 if (ec) 124 { 125 return; // threshold not supported 126 } 127 128 auto directionFind = result.find("Direction"); 129 auto severityFind = result.find("Severity"); 130 auto valueFind = result.find("Value"); 131 if (valueFind == result.end() || severityFind == result.end() || 132 directionFind == result.end()) 133 { 134 std::cerr << "Malformed threshold in configuration\n"; 135 return; 136 } 137 unsigned int level = mapbox::util::apply_visitor( 138 VariantToUnsignedIntVisitor(), severityFind->second); 139 140 std::string dir = mapbox::util::apply_visitor( 141 VariantToStringVisitor(), directionFind->second); 142 if ((toBusValue(threshold.level) != level) || 143 (toBusValue(threshold.direction) != dir)) 144 { 145 return; // not the droid we're looking for 146 } 147 148 sdbusplus::message::variant<double> value(threshold.value); 149 conn->async_method_call( 150 [](const boost::system::error_code &ec) { 151 if (ec) 152 { 153 std::cerr << "Error setting threshold " << ec 154 << "\n"; 155 } 156 }, 157 ENTITY_MANAGER_NAME, path, 158 "org.freedesktop.DBus.Properties", "Set", 159 thresholdInterface, "Value", value); 160 }, 161 ENTITY_MANAGER_NAME, path, "org.freedesktop.DBus.Properties", 162 "GetAll", thresholdInterface); 163 } 164 } 165 166 void checkThresholds(Sensor *sensor) 167 { 168 169 if (sensor->thresholds.empty()) 170 { 171 return; 172 } 173 for (auto &threshold : sensor->thresholds) 174 { 175 if (threshold.direction == thresholds::Direction::HIGH) 176 { 177 if (sensor->value > threshold.value && !threshold.asserted) 178 { 179 assertThresholds(sensor, threshold.level, threshold.direction, 180 true); 181 threshold.asserted = true; 182 } 183 else if (sensor->value <= threshold.value && threshold.asserted) 184 { 185 assertThresholds(sensor, threshold.level, threshold.direction, 186 false); 187 threshold.asserted = false; 188 } 189 } 190 else 191 { 192 if (sensor->value < threshold.value && !threshold.asserted) 193 { 194 assertThresholds(sensor, threshold.level, threshold.direction, 195 true); 196 threshold.asserted = true; 197 } 198 else if (sensor->value >= threshold.value && threshold.asserted) 199 { 200 assertThresholds(sensor, threshold.level, threshold.direction, 201 false); 202 threshold.asserted = false; 203 } 204 } 205 } 206 } 207 208 void assertThresholds(Sensor *sensor, thresholds::Level level, 209 thresholds::Direction direction, bool assert) 210 { 211 std::string property; 212 std::shared_ptr<sdbusplus::asio::dbus_interface> interface; 213 if (level == thresholds::Level::WARNING && 214 direction == thresholds::Direction::HIGH) 215 { 216 property = "WarningAlarmHigh"; 217 interface = sensor->thresholdInterfaceWarning; 218 } 219 else if (level == thresholds::Level::WARNING && 220 direction == thresholds::Direction::LOW) 221 { 222 property = "WarningAlarmLow"; 223 interface = sensor->thresholdInterfaceWarning; 224 } 225 else if (level == thresholds::Level::CRITICAL && 226 direction == thresholds::Direction::HIGH) 227 { 228 property = "CriticalAlarmHigh"; 229 interface = sensor->thresholdInterfaceCritical; 230 } 231 else if (level == thresholds::Level::CRITICAL && 232 direction == thresholds::Direction::LOW) 233 { 234 property = "CriticalAlarmLow"; 235 interface = sensor->thresholdInterfaceCritical; 236 } 237 else 238 { 239 std::cerr << "Unknown threshold, level " << level << "direction " 240 << direction << "\n"; 241 return; 242 } 243 if (!interface) 244 { 245 std::cout << "trying to set uninitialized interface\n"; 246 return; 247 } 248 interface->set_property(property, assert); 249 } 250 251 static constexpr std::array<const char *, 4> ATTR_TYPES = {"lcrit", "min", 252 "max", "crit"}; 253 254 bool ParseThresholdsFromAttr( 255 std::vector<thresholds::Threshold> &threshold_vector, 256 const std::string &input_path, const double scale_factor) 257 { 258 for (auto &type : ATTR_TYPES) 259 { 260 auto attr_path = boost::replace_all_copy(input_path, "input", type); 261 std::ifstream attr_file(attr_path); 262 if (!attr_file.good()) 263 continue; 264 std::string attr; 265 std::getline(attr_file, attr); 266 attr_file.close(); 267 268 Level level; 269 Direction direction; 270 double val = std::stod(attr) / scale_factor; 271 if (type == "min" || type == "max") 272 level = Level::WARNING; 273 else 274 level = Level::CRITICAL; 275 if (type == "min" || type == "lcrit") 276 direction = Direction::LOW; 277 else 278 direction = Direction::HIGH; 279 280 if (DEBUG) 281 std::cout << "Threshold: " << attr_path << ": " << val << "\n"; 282 283 threshold_vector.emplace_back(level, direction, val); 284 } 285 // no thresholds is allowed, not an error so return true always 286 return true; 287 } 288 289 bool HasCriticalInterface( 290 const std::vector<thresholds::Threshold> &threshold_vector) 291 { 292 for (auto &threshold : threshold_vector) 293 { 294 if (threshold.level == Level::CRITICAL) 295 return true; 296 } 297 return false; 298 } 299 300 bool HasWarningInterface( 301 const std::vector<thresholds::Threshold> &threshold_vector) 302 { 303 for (auto &threshold : threshold_vector) 304 { 305 if (threshold.level == Level::WARNING) 306 return true; 307 } 308 return false; 309 } 310 } // namespace thresholds 311