blob: a15da17378903cde20b9a77875950a6f213cd935 [file] [log] [blame]
#include <Thresholds.hpp>
#include <VariantVisitors.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/lexical_cast.hpp>
#include <cmath>
#include <fstream>
#include <iostream>
#include <sensor.hpp>
static constexpr bool DEBUG = false;
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 (std::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 (std::visit(VariantToUnsignedIntVisitor(), severityFind->second) ==
0)
{
level = Level::WARNING;
}
else
{
level = Level::CRITICAL;
}
if (std::visit(VariantToStringVisitor(), directionFind->second) ==
"less than")
{
direction = Direction::LOW;
}
else
{
direction = Direction::HIGH;
}
double val = std::visit(VariantToDoubleVisitor(), 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,
size_t thresholdCount)
{
for (int ii = 0; ii < thresholdCount; 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 = std::visit(VariantToUnsignedIntVisitor(),
severityFind->second);
std::string dir =
std::visit(VariantToStringVisitor(), directionFind->second);
if ((toBusValue(threshold.level) != level) ||
(toBusValue(threshold.direction) != dir))
{
return; // not the droid we're looking for
}
std::variant<double> value(threshold.value);
conn->async_method_call(
[](const boost::system::error_code& ec) {
if (ec)
{
std::cerr << "Error setting threshold " << ec
<< "\n";
}
},
entityManagerName, path, "org.freedesktop.DBus.Properties",
"Set", thresholdInterface, "Value", value);
},
entityManagerName, path, "org.freedesktop.DBus.Properties",
"GetAll", thresholdInterface);
}
}
void updateThresholds(Sensor* sensor)
{
if (sensor->thresholds.empty())
{
return;
}
for (const auto& threshold : sensor->thresholds)
{
std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
std::string property;
if (threshold.level == thresholds::Level::CRITICAL)
{
interface = sensor->thresholdInterfaceCritical;
if (threshold.direction == thresholds::Direction::HIGH)
{
property = "CriticalHigh";
}
else
{
property = "CriticalLow";
}
}
else if (threshold.level == thresholds::Level::WARNING)
{
interface = sensor->thresholdInterfaceWarning;
if (threshold.direction == thresholds::Direction::HIGH)
{
property = "WarningHigh";
}
else
{
property = "WarningLow";
}
}
else
{
continue;
}
if (!interface)
{
continue;
}
interface->set_property(property, threshold.value);
}
}
static std::vector<std::pair<Threshold, bool>> checkThresholds(Sensor* sensor,
double value)
{
std::vector<std::pair<Threshold, bool>> thresholdChanges;
if (sensor->thresholds.empty())
{
return thresholdChanges;
}
for (auto& threshold : sensor->thresholds)
{
if (threshold.direction == thresholds::Direction::HIGH)
{
if (value > threshold.value)
{
thresholdChanges.push_back(std::make_pair(threshold, true));
}
else if (value <= threshold.value)
{
thresholdChanges.push_back(std::make_pair(threshold, false));
}
}
else
{
if (value < threshold.value)
{
thresholdChanges.push_back(std::make_pair(threshold, true));
}
else if (value >= threshold.value)
{
thresholdChanges.push_back(std::make_pair(threshold, false));
}
}
}
return thresholdChanges;
}
bool checkThresholds(Sensor* sensor)
{
bool status = true;
std::vector<std::pair<Threshold, bool>> changes =
checkThresholds(sensor, sensor->value);
for (const auto& [threshold, asserted] : changes)
{
assertThresholds(sensor, threshold.level, threshold.direction,
asserted);
if (threshold.level == thresholds::Level::CRITICAL && asserted)
{
status = false;
}
}
return status;
}
void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer)
{
std::vector<std::pair<Threshold, bool>> changes =
checkThresholds(sensor, sensor->value);
for (const auto& [threshold, asserted] : changes)
{
if (asserted)
{
thresholdTimer.startTimer(threshold);
}
else
{
assertThresholds(sensor, threshold.level, threshold.direction,
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> attrTypes = {"lcrit", "min", "max",
"crit"};
bool parseThresholdsFromAttr(
std::vector<thresholds::Threshold>& thresholdVector,
const std::string& inputPath, const double& scaleFactor)
{
for (auto& type : attrTypes)
{
auto attrPath = boost::replace_all_copy(inputPath, "input", type);
std::ifstream attrFile(attrPath);
if (!attrFile.good())
{
continue;
}
std::string attr;
std::getline(attrFile, attr);
attrFile.close();
Level level;
Direction direction;
double val = std::stod(attr) / scaleFactor;
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: " << attrPath << ": " << val << "\n";
}
thresholdVector.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>& thresholdVector)
{
for (auto& threshold : thresholdVector)
{
if (threshold.level == Level::CRITICAL)
{
return true;
}
}
return false;
}
bool hasWarningInterface(
const std::vector<thresholds::Threshold>& thresholdVector)
{
for (auto& threshold : thresholdVector)
{
if (threshold.level == Level::WARNING)
{
return true;
}
}
return false;
}
} // namespace thresholds