blob: 501a035a263d56f8387fc1ec8fc9c2afda6d30d6 [file] [log] [blame]
Matt Spinlerc09b8c82021-08-05 16:29:47 -05001/**
2 * Copyright © 2021 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "modifier.hpp"
18
Matt Spinlerc2727902022-02-02 11:13:09 -060019#include "json/config_base.hpp"
Matt Spinlerc09b8c82021-08-05 16:29:47 -050020#include "json/manager.hpp"
21
22#include <fmt/format.h>
23
24#include <phosphor-logging/log.hpp>
25
26using namespace phosphor::logging;
27
28namespace phosphor::fan::control::json
29{
30
31/**
32 * @brief Variant visitor to return a value of the template type specified.
33 */
34template <typename T>
35struct ToTypeVisitor
36{
37 template <typename U>
38 T operator()(const U& t) const
39 {
40 if constexpr (std::is_arithmetic_v<U> && std::is_arithmetic_v<T>)
41 {
42 return static_cast<T>(t);
43 }
44 throw std::invalid_argument(
45 "Non arithmetic type used in ToTypeVisitor");
46 }
47};
48
49/**
Matt Spinlerc2727902022-02-02 11:13:09 -060050 * @brief Return a default value to use when the argument passed
51 * to LessThanOperator is out of range.
52 */
Matthew Barth6941fd72022-03-10 16:50:30 -060053PropertyVariantType
54 getDefaultValue(const PropertyVariantType& val,
55 const std::optional<PropertyVariantType>& defaultValue)
Matt Spinlerc2727902022-02-02 11:13:09 -060056{
Matthew Barth6941fd72022-03-10 16:50:30 -060057 // When a default value is given, return that
58 if (defaultValue)
59 {
60 return *defaultValue;
61 }
62
Matt Spinlerc2727902022-02-02 11:13:09 -060063 if (std::holds_alternative<bool>(val))
64 {
65 return false;
66 }
67 else if (std::holds_alternative<std::string>(val))
68 {
69 return std::string{};
70 }
71 else if (std::holds_alternative<double>(val))
72 {
73 return std::numeric_limits<double>::quiet_NaN();
74 }
75 else if (std::holds_alternative<int32_t>(val))
76 {
77 return std::numeric_limits<int32_t>::quiet_NaN();
78 }
79 else if (std::holds_alternative<int64_t>(val))
80 {
81 return std::numeric_limits<int64_t>::quiet_NaN();
82 }
83
84 throw std::runtime_error(
85 "Invalid variant type when determining default value");
86}
87
88/**
Matt Spinlerc09b8c82021-08-05 16:29:47 -050089 * @brief Implements the minus operator to subtract two values.
90 *
91 * With strings values, A - B removes all occurrences of B in A.
92 * Throws if the type is a bool.
93 */
94struct MinusOperator : public Modifier::BaseOperator
95{
Matthew Barth6941fd72022-03-10 16:50:30 -060096 MinusOperator(const json& jsonObj) :
97 arg(ConfigBase::getJsonValue(jsonObj["value"]))
Matt Spinlerc2727902022-02-02 11:13:09 -060098 {}
99
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500100 PropertyVariantType operator()(double val) override
101 {
102 return val - std::visit(ToTypeVisitor<double>(), arg);
103 }
104
105 PropertyVariantType operator()(int32_t val) override
106 {
107 return val - std::visit(ToTypeVisitor<int32_t>(), arg);
108 }
109
110 PropertyVariantType operator()(int64_t val) override
111 {
112 return val - std::visit(ToTypeVisitor<int64_t>(), arg);
113 }
114
115 PropertyVariantType operator()(const std::string& val) override
116 {
117 // Remove all occurrences of arg from val.
118 auto value = val;
119 auto toRemove = std::get<std::string>(arg);
120 size_t pos;
121 while ((pos = value.find(toRemove)) != std::string::npos)
122 {
123 value.erase(pos, toRemove.size());
124 }
125
126 return value;
127 }
128
Mike Cappsb2e9a4f2022-06-13 10:15:42 -0400129 PropertyVariantType operator()(bool) override
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500130 {
131 throw std::runtime_error{
132 "Bool not allowed as a 'minus' modifier value"};
133 }
134
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500135 PropertyVariantType arg;
136};
137
Matt Spinlerc2727902022-02-02 11:13:09 -0600138/**
Matthew Barth6941fd72022-03-10 16:50:30 -0600139 * @brief Implements an operator to return a value specified in the JSON that is
140 * chosen based on if the value passed into the operator is less than the lowest
141 * arg_value it is true for or the given `default_value` if not found to be less
142 * than any entries.
Matt Spinlerc2727902022-02-02 11:13:09 -0600143 *
144 * "modifier": {
145 * "operator": "less_than",
Matthew Barth6941fd72022-03-10 16:50:30 -0600146 * "default_value": 1000, // OPTIONAL
Matt Spinlerc2727902022-02-02 11:13:09 -0600147 * "value": [
148 * {
149 * "arg_value": 30, // if value is less than 30
150 * "parameter_value": 300 // then return 300
151 * },
152 * {
153 * "arg_value": 40, // else if value is less than 40
154 * "parameter_value": 400 // then return 400
155 * },
156 * ]
157 * }
158 *
Matthew Barth6941fd72022-03-10 16:50:30 -0600159 * If the value passed in is higher than the highest arg_value, it returns a
160 * default value this is the `default_value` given or based on the data type of
161 * parameter_value.
Matt Spinlerc2727902022-02-02 11:13:09 -0600162 */
163struct LessThanOperator : public Modifier::BaseOperator
164{
Matthew Barth6941fd72022-03-10 16:50:30 -0600165 LessThanOperator(const json& jsonObj)
Matt Spinlerc2727902022-02-02 11:13:09 -0600166 {
Matthew Barth6941fd72022-03-10 16:50:30 -0600167 const auto& valueArray = jsonObj["value"];
Matt Spinlerc2727902022-02-02 11:13:09 -0600168 if (!valueArray.is_array())
169 {
170 log<level::ERR>(
171 fmt::format("Invalid JSON data for less_than config: {}",
172 valueArray.dump())
173 .c_str());
174 throw std::invalid_argument("Invalid modifier JSON");
175 }
176
177 for (const auto& valueEntry : valueArray)
178 {
179 if (!valueEntry.contains("arg_value") ||
180 !valueEntry.contains("parameter_value"))
181 {
182 log<level::ERR>(
183 fmt::format("Missing arg_value or parameter_value keys "
184 "in less_than config: {}",
185 valueArray.dump())
186 .c_str());
187 throw std::invalid_argument("Invalid modifier JSON");
188 }
189
190 auto argVal = ConfigBase::getJsonValue(valueEntry.at("arg_value"));
191
192 if (std::holds_alternative<bool>(argVal))
193 {
194 log<level::ERR>(
195 fmt::format(
196 "Invalid data type in arg_value key in modifier JSON "
197 "config: {}",
198 valueArray.dump())
199 .c_str());
200 throw std::invalid_argument("Invalid modifier JSON");
201 }
202
203 auto paramVal =
204 ConfigBase::getJsonValue(valueEntry.at("parameter_value"));
205
206 rangeValues.emplace_back(argVal, paramVal);
207 }
208
209 if (rangeValues.empty())
210 {
211 log<level::ERR>(fmt::format("No valid range values found in "
212 "modifier json: {}",
213 valueArray.dump())
214 .c_str());
215 throw std::invalid_argument("Invalid modifier JSON");
216 }
Matthew Barth6941fd72022-03-10 16:50:30 -0600217
218 if (jsonObj.contains("default_value"))
219 {
220 defaultValue = ConfigBase::getJsonValue(jsonObj["default_value"]);
221 }
Matt Spinlerc2727902022-02-02 11:13:09 -0600222 }
223
224 PropertyVariantType operator()(double val) override
225 {
226 for (const auto& rangeValue : rangeValues)
227 {
228 if (val < std::visit(ToTypeVisitor<double>(), rangeValue.first))
229 {
230 return rangeValue.second;
231 }
232 }
233 // Return a default value based on last entry type
Matthew Barth6941fd72022-03-10 16:50:30 -0600234 return getDefaultValue(rangeValues.back().second, defaultValue);
Matt Spinlerc2727902022-02-02 11:13:09 -0600235 }
236
237 PropertyVariantType operator()(int32_t val) override
238 {
239 for (const auto& rangeValue : rangeValues)
240 {
241 if (val < std::visit(ToTypeVisitor<int32_t>(), rangeValue.first))
242 {
243 return rangeValue.second;
244 }
245 }
Matthew Barth6941fd72022-03-10 16:50:30 -0600246 return getDefaultValue(rangeValues.back().second, defaultValue);
Matt Spinlerc2727902022-02-02 11:13:09 -0600247 }
248
249 PropertyVariantType operator()(int64_t val) override
250 {
251 for (const auto& rangeValue : rangeValues)
252 {
253 if (val < std::visit(ToTypeVisitor<int64_t>(), rangeValue.first))
254 {
255 return rangeValue.second;
256 }
257 }
Matthew Barth6941fd72022-03-10 16:50:30 -0600258 return getDefaultValue(rangeValues.back().second, defaultValue);
Matt Spinlerc2727902022-02-02 11:13:09 -0600259 }
260
261 PropertyVariantType operator()(const std::string& val) override
262 {
263 for (const auto& rangeValue : rangeValues)
264 {
265 if (val <
266 std::visit(ToTypeVisitor<std::string>(), rangeValue.first))
267 {
268 return rangeValue.second;
269 }
270 }
Matthew Barth6941fd72022-03-10 16:50:30 -0600271 return getDefaultValue(rangeValues.back().second, defaultValue);
Matt Spinlerc2727902022-02-02 11:13:09 -0600272 }
273
Mike Cappsb2e9a4f2022-06-13 10:15:42 -0400274 PropertyVariantType operator()(bool) override
Matt Spinlerc2727902022-02-02 11:13:09 -0600275 {
276 throw std::runtime_error{
277 "Bool not allowed as a 'less_than' modifier value"};
278 }
279
280 std::vector<std::pair<PropertyVariantType, PropertyVariantType>>
281 rangeValues;
Matthew Barth6941fd72022-03-10 16:50:30 -0600282 std::optional<PropertyVariantType> defaultValue;
Matt Spinlerc2727902022-02-02 11:13:09 -0600283};
284
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500285Modifier::Modifier(const json& jsonObj)
286{
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500287 setOperator(jsonObj);
288}
289
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500290void Modifier::setOperator(const json& jsonObj)
291{
Matt Spinlerc2727902022-02-02 11:13:09 -0600292 if (!jsonObj.contains("operator") || !jsonObj.contains("value"))
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500293 {
294 log<level::ERR>(
Matt Spinlerc2727902022-02-02 11:13:09 -0600295 fmt::format(
296 "Modifier entry in JSON missing 'operator' or 'value': {}",
297 jsonObj.dump())
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500298 .c_str());
299 throw std::invalid_argument("Invalid modifier JSON");
300 }
301
302 auto op = jsonObj["operator"].get<std::string>();
303
304 if (op == "minus")
305 {
Matthew Barth6941fd72022-03-10 16:50:30 -0600306 _operator = std::make_unique<MinusOperator>(jsonObj);
Matt Spinlerc2727902022-02-02 11:13:09 -0600307 }
308 else if (op == "less_than")
309 {
Matthew Barth6941fd72022-03-10 16:50:30 -0600310 _operator = std::make_unique<LessThanOperator>(jsonObj);
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500311 }
312 else
313 {
314 log<level::ERR>(fmt::format("Invalid operator in the modifier JSON: {}",
315 jsonObj.dump())
316 .c_str());
317 throw std::invalid_argument("Invalid operator in the modifier JSON");
318 }
319}
320
321PropertyVariantType Modifier::doOp(const PropertyVariantType& val)
322{
323 return std::visit(*_operator, val);
324}
325
326} // namespace phosphor::fan::control::json