control: Add 'less_than' modifier
This modifier allows one to return a value from a table based on if the
value passed to it is less than the table key value.
For example:
"modifier": {
"operator": "less_than",
"value": [
{
"arg_value": 30,
"parameter_value": 300
},
{
"arg_value": 40,
"parameter_value": 400
},
{
"arg_value": 50,
"parameter_value": 500
}
]
}
With the above config, it would do the following:
* value less than 30: return 300
* else if value less than 40: return 400
* else if value less than 50: return 500
* else: return a default value based on data type of parameter_value
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I8b47341f41723c0cdf1ee2786526550fc24551f9
diff --git a/control/json/config_base.hpp b/control/json/config_base.hpp
index 4f7a425..5bfb9f2 100644
--- a/control/json/config_base.hpp
+++ b/control/json/config_base.hpp
@@ -102,7 +102,6 @@
return _profiles;
}
- protected:
/**
* @brief Determines the data type of a JSON configured parameter that is
* used as a variant within the fan control application and returns the
@@ -143,6 +142,7 @@
"Unsupported data type for JSON object's value");
}
+ protected:
/* Name of the configuration object */
std::string _name;
diff --git a/control/json/utils/modifier.cpp b/control/json/utils/modifier.cpp
index 6d00bc1..6a9d278 100644
--- a/control/json/utils/modifier.cpp
+++ b/control/json/utils/modifier.cpp
@@ -16,6 +16,7 @@
#include "modifier.hpp"
+#include "json/config_base.hpp"
#include "json/manager.hpp"
#include <fmt/format.h>
@@ -46,6 +47,37 @@
};
/**
+ * @brief Return a default value to use when the argument passed
+ * to LessThanOperator is out of range.
+ */
+PropertyVariantType getDefaultValue(const PropertyVariantType& val)
+{
+ if (std::holds_alternative<bool>(val))
+ {
+ return false;
+ }
+ else if (std::holds_alternative<std::string>(val))
+ {
+ return std::string{};
+ }
+ else if (std::holds_alternative<double>(val))
+ {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+ else if (std::holds_alternative<int32_t>(val))
+ {
+ return std::numeric_limits<int32_t>::quiet_NaN();
+ }
+ else if (std::holds_alternative<int64_t>(val))
+ {
+ return std::numeric_limits<int64_t>::quiet_NaN();
+ }
+
+ throw std::runtime_error(
+ "Invalid variant type when determining default value");
+}
+
+/**
* @brief Implements the minus operator to subtract two values.
*
* With strings values, A - B removes all occurrences of B in A.
@@ -53,6 +85,10 @@
*/
struct MinusOperator : public Modifier::BaseOperator
{
+ MinusOperator(const json& valueObj) :
+ arg(ConfigBase::getJsonValue(valueObj))
+ {}
+
PropertyVariantType operator()(double val) override
{
return val - std::visit(ToTypeVisitor<double>(), arg);
@@ -88,64 +124,161 @@
"Bool not allowed as a 'minus' modifier value"};
}
- MinusOperator(PropertyVariantType& arg) : arg(arg)
- {}
-
PropertyVariantType arg;
};
+/**
+ * @brief Implements an operator to return a value specified in
+ * the JSON that is chosen based on if the value passed
+ * into the operator is less than the lowest arg_value it
+ * is true for.
+ *
+ * "modifier": {
+ * "operator": "less_than",
+ * "value": [
+ * {
+ * "arg_value": 30, // if value is less than 30
+ * "parameter_value": 300 // then return 300
+ * },
+ * {
+ * "arg_value": 40, // else if value is less than 40
+ * "parameter_value": 400 // then return 400
+ * },
+ * ]
+ * }
+ *
+ * If the value passed in is higher than the highest arg_value,
+ * it returns a default value this is chosen based on the
+ * data type of parameter_value.
+ */
+struct LessThanOperator : public Modifier::BaseOperator
+{
+ LessThanOperator(const json& valueArray)
+ {
+ if (!valueArray.is_array())
+ {
+ log<level::ERR>(
+ fmt::format("Invalid JSON data for less_than config: {}",
+ valueArray.dump())
+ .c_str());
+ throw std::invalid_argument("Invalid modifier JSON");
+ }
+
+ for (const auto& valueEntry : valueArray)
+ {
+ if (!valueEntry.contains("arg_value") ||
+ !valueEntry.contains("parameter_value"))
+ {
+ log<level::ERR>(
+ fmt::format("Missing arg_value or parameter_value keys "
+ "in less_than config: {}",
+ valueArray.dump())
+ .c_str());
+ throw std::invalid_argument("Invalid modifier JSON");
+ }
+
+ auto argVal = ConfigBase::getJsonValue(valueEntry.at("arg_value"));
+
+ if (std::holds_alternative<bool>(argVal))
+ {
+ log<level::ERR>(
+ fmt::format(
+ "Invalid data type in arg_value key in modifier JSON "
+ "config: {}",
+ valueArray.dump())
+ .c_str());
+ throw std::invalid_argument("Invalid modifier JSON");
+ }
+
+ auto paramVal =
+ ConfigBase::getJsonValue(valueEntry.at("parameter_value"));
+
+ rangeValues.emplace_back(argVal, paramVal);
+ }
+
+ if (rangeValues.empty())
+ {
+ log<level::ERR>(fmt::format("No valid range values found in "
+ "modifier json: {}",
+ valueArray.dump())
+ .c_str());
+ throw std::invalid_argument("Invalid modifier JSON");
+ }
+ }
+
+ PropertyVariantType operator()(double val) override
+ {
+ for (const auto& rangeValue : rangeValues)
+ {
+ if (val < std::visit(ToTypeVisitor<double>(), rangeValue.first))
+ {
+ return rangeValue.second;
+ }
+ }
+ // Return a default value based on last entry type
+ return getDefaultValue(rangeValues.back().second);
+ }
+
+ PropertyVariantType operator()(int32_t val) override
+ {
+ for (const auto& rangeValue : rangeValues)
+ {
+ if (val < std::visit(ToTypeVisitor<int32_t>(), rangeValue.first))
+ {
+ return rangeValue.second;
+ }
+ }
+ return getDefaultValue(rangeValues.back().second);
+ }
+
+ PropertyVariantType operator()(int64_t val) override
+ {
+ for (const auto& rangeValue : rangeValues)
+ {
+ if (val < std::visit(ToTypeVisitor<int64_t>(), rangeValue.first))
+ {
+ return rangeValue.second;
+ }
+ }
+ return getDefaultValue(rangeValues.back().second);
+ }
+
+ PropertyVariantType operator()(const std::string& val) override
+ {
+ for (const auto& rangeValue : rangeValues)
+ {
+ if (val <
+ std::visit(ToTypeVisitor<std::string>(), rangeValue.first))
+ {
+ return rangeValue.second;
+ }
+ }
+ return getDefaultValue(rangeValues.back().second);
+ }
+
+ PropertyVariantType operator()(bool val) override
+ {
+ throw std::runtime_error{
+ "Bool not allowed as a 'less_than' modifier value"};
+ }
+
+ std::vector<std::pair<PropertyVariantType, PropertyVariantType>>
+ rangeValues;
+};
+
Modifier::Modifier(const json& jsonObj)
{
- setValue(jsonObj);
setOperator(jsonObj);
}
-void Modifier::setValue(const json& jsonObj)
-{
- if (!jsonObj.contains("value"))
- {
- log<level::ERR>(
- fmt::format("Modifier entry in JSON missing 'value': {}",
- jsonObj.dump())
- .c_str());
- throw std::invalid_argument("Invalid modifier JSON");
- }
-
- const auto& object = jsonObj.at("value");
- if (auto boolPtr = object.get_ptr<const bool*>())
- {
- _value = *boolPtr;
- }
- else if (auto intPtr = object.get_ptr<const int64_t*>())
- {
- _value = *intPtr;
- }
- else if (auto doublePtr = object.get_ptr<const double*>())
- {
- _value = *doublePtr;
- }
- else if (auto stringPtr = object.get_ptr<const std::string*>())
- {
- _value = *stringPtr;
- }
- else
- {
- log<level::ERR>(
- fmt::format(
- "Invalid JSON type for value property in modifer json: {}",
- jsonObj.dump())
- .c_str());
- throw std::invalid_argument("Invalid modifier JSON");
- }
-}
-
void Modifier::setOperator(const json& jsonObj)
{
- if (!jsonObj.contains("operator"))
+ if (!jsonObj.contains("operator") || !jsonObj.contains("value"))
{
log<level::ERR>(
- fmt::format("Modifier entry in JSON missing 'operator': {}",
- jsonObj.dump())
+ fmt::format(
+ "Modifier entry in JSON missing 'operator' or 'value': {}",
+ jsonObj.dump())
.c_str());
throw std::invalid_argument("Invalid modifier JSON");
}
@@ -154,7 +287,11 @@
if (op == "minus")
{
- _operator = std::make_unique<MinusOperator>(_value);
+ _operator = std::make_unique<MinusOperator>(jsonObj["value"]);
+ }
+ else if (op == "less_than")
+ {
+ _operator = std::make_unique<LessThanOperator>(jsonObj["value"]);
}
else
{
diff --git a/control/json/utils/modifier.hpp b/control/json/utils/modifier.hpp
index 3f59246..6153279 100644
--- a/control/json/utils/modifier.hpp
+++ b/control/json/utils/modifier.hpp
@@ -42,6 +42,7 @@
*
* The valid operators are:
* - "minus"
+ * - "less_than"
*
* To add a new operator, derive a new class from BaseOperator and
* then create it accordingly in setOperator.
@@ -103,13 +104,6 @@
*/
void setOperator(const json& jsonObj);
- /**
- * @brief Subtracts _value from value
- *
- * @param[in] value - The value to subtract from
- */
- PropertyVariantType minus(const PropertyVariantType& value);
-
/** @brief The value used by the operator */
PropertyVariantType _value;