control: Add Modifier utility
Create a Modifier utility class that provides a doOp() function that can
modify a value based on the JSON config passed to its constructor.
With the JSON:
{
"operator": "minus",
"value": 3
}
doOp() will subtract 3 from the passed in value.
The only currently supported expression is 'minus'.
This will be used by future actions to read a D-Bus property, subtract
some configurable value from it, and store it in the Manager's
parameter store.
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I31d9800f83e52a927a2061a9a896da9c19d35809
diff --git a/control/Makefile.am b/control/Makefile.am
index aca9c5c..d78ec47 100644
--- a/control/Makefile.am
+++ b/control/Makefile.am
@@ -70,7 +70,8 @@
json/actions/net_target_increase.cpp \
json/actions/net_target_decrease.cpp \
json/actions/timer_based_actions.cpp \
- json/actions/mapped_floor.cpp
+ json/actions/mapped_floor.cpp \
+ json/utils/modifier.cpp
else
phosphor_fan_control_SOURCES += \
argument.cpp \
diff --git a/control/json/utils/modifier.cpp b/control/json/utils/modifier.cpp
new file mode 100644
index 0000000..6d00bc1
--- /dev/null
+++ b/control/json/utils/modifier.cpp
@@ -0,0 +1,173 @@
+/**
+ * Copyright © 2021 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "modifier.hpp"
+
+#include "json/manager.hpp"
+
+#include <fmt/format.h>
+
+#include <phosphor-logging/log.hpp>
+
+using namespace phosphor::logging;
+
+namespace phosphor::fan::control::json
+{
+
+/**
+ * @brief Variant visitor to return a value of the template type specified.
+ */
+template <typename T>
+struct ToTypeVisitor
+{
+ template <typename U>
+ T operator()(const U& t) const
+ {
+ if constexpr (std::is_arithmetic_v<U> && std::is_arithmetic_v<T>)
+ {
+ return static_cast<T>(t);
+ }
+ throw std::invalid_argument(
+ "Non arithmetic type used in ToTypeVisitor");
+ }
+};
+
+/**
+ * @brief Implements the minus operator to subtract two values.
+ *
+ * With strings values, A - B removes all occurrences of B in A.
+ * Throws if the type is a bool.
+ */
+struct MinusOperator : public Modifier::BaseOperator
+{
+ PropertyVariantType operator()(double val) override
+ {
+ return val - std::visit(ToTypeVisitor<double>(), arg);
+ }
+
+ PropertyVariantType operator()(int32_t val) override
+ {
+ return val - std::visit(ToTypeVisitor<int32_t>(), arg);
+ }
+
+ PropertyVariantType operator()(int64_t val) override
+ {
+ return val - std::visit(ToTypeVisitor<int64_t>(), arg);
+ }
+
+ PropertyVariantType operator()(const std::string& val) override
+ {
+ // Remove all occurrences of arg from val.
+ auto value = val;
+ auto toRemove = std::get<std::string>(arg);
+ size_t pos;
+ while ((pos = value.find(toRemove)) != std::string::npos)
+ {
+ value.erase(pos, toRemove.size());
+ }
+
+ return value;
+ }
+
+ PropertyVariantType operator()(bool val) override
+ {
+ throw std::runtime_error{
+ "Bool not allowed as a 'minus' modifier value"};
+ }
+
+ MinusOperator(PropertyVariantType& arg) : arg(arg)
+ {}
+
+ PropertyVariantType arg;
+};
+
+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"))
+ {
+ log<level::ERR>(
+ fmt::format("Modifier entry in JSON missing 'operator': {}",
+ jsonObj.dump())
+ .c_str());
+ throw std::invalid_argument("Invalid modifier JSON");
+ }
+
+ auto op = jsonObj["operator"].get<std::string>();
+
+ if (op == "minus")
+ {
+ _operator = std::make_unique<MinusOperator>(_value);
+ }
+ else
+ {
+ log<level::ERR>(fmt::format("Invalid operator in the modifier JSON: {}",
+ jsonObj.dump())
+ .c_str());
+ throw std::invalid_argument("Invalid operator in the modifier JSON");
+ }
+}
+
+PropertyVariantType Modifier::doOp(const PropertyVariantType& val)
+{
+ return std::visit(*_operator, val);
+}
+
+} // namespace phosphor::fan::control::json
diff --git a/control/json/utils/modifier.hpp b/control/json/utils/modifier.hpp
new file mode 100644
index 0000000..3f59246
--- /dev/null
+++ b/control/json/utils/modifier.hpp
@@ -0,0 +1,120 @@
+/**
+ * Copyright © 2021 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "config_base.hpp"
+
+#include <string>
+#include <vector>
+
+namespace phosphor::fan::control::json
+{
+
+using json = nlohmann::json;
+
+/**
+ * @class Modifier
+ *
+ * This class provides a doOp() function to modify a PropertyVariantType value
+ * based on a JSON config passed into its constructor.
+ *
+ * For example, with the JSON:
+ * {
+ * "operator": "minus",
+ * "value": 3
+ * }
+ *
+ * When doOp() is called, it will subtract 3 from the value passed
+ * into doOp() and return the result.
+ *
+ * The valid operators are:
+ * - "minus"
+ *
+ * To add a new operator, derive a new class from BaseOperator and
+ * then create it accordingly in setOperator.
+ */
+class Modifier
+{
+ public:
+ /**
+ * @brief Base class for operators
+ */
+ struct BaseOperator
+ {
+ virtual PropertyVariantType operator()(double val) = 0;
+
+ virtual PropertyVariantType operator()(int32_t val) = 0;
+
+ virtual PropertyVariantType operator()(int64_t val) = 0;
+
+ virtual PropertyVariantType operator()(const std::string& val) = 0;
+
+ virtual PropertyVariantType operator()(bool val) = 0;
+ };
+
+ Modifier() = delete;
+ ~Modifier() = default;
+ Modifier(const Modifier&) = delete;
+ Modifier& operator=(const Modifier&) = delete;
+ Modifier(Modifier&&) = delete;
+ Modifier& operator=(Modifier&&) = delete;
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] jsonObj - The JSON config object
+ */
+ Modifier(const json& jsonObj);
+
+ /**
+ * @brief Performs the operation
+ *
+ * @param[in] value - The variant to do the operation on
+ *
+ * @return PropertyVariantType - The result
+ */
+ PropertyVariantType doOp(const PropertyVariantType& value);
+
+ private:
+ /**
+ * @brief Parse and set the value
+ *
+ * @param[in] jsonObj - The JSON config object
+ */
+ void setValue(const json& jsonObj);
+
+ /**
+ * @brief Parse and set the operator
+ *
+ * @param[in] jsonObj - The JSON config object
+ */
+ 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;
+
+ /** @brief The operator that will be used */
+ std::unique_ptr<BaseOperator> _operator;
+};
+
+} // namespace phosphor::fan::control::json