blob: 6a9d2781e63ede8bf6e256fbaf1e9f9c2353557c [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 */
53PropertyVariantType getDefaultValue(const PropertyVariantType& val)
54{
55 if (std::holds_alternative<bool>(val))
56 {
57 return false;
58 }
59 else if (std::holds_alternative<std::string>(val))
60 {
61 return std::string{};
62 }
63 else if (std::holds_alternative<double>(val))
64 {
65 return std::numeric_limits<double>::quiet_NaN();
66 }
67 else if (std::holds_alternative<int32_t>(val))
68 {
69 return std::numeric_limits<int32_t>::quiet_NaN();
70 }
71 else if (std::holds_alternative<int64_t>(val))
72 {
73 return std::numeric_limits<int64_t>::quiet_NaN();
74 }
75
76 throw std::runtime_error(
77 "Invalid variant type when determining default value");
78}
79
80/**
Matt Spinlerc09b8c82021-08-05 16:29:47 -050081 * @brief Implements the minus operator to subtract two values.
82 *
83 * With strings values, A - B removes all occurrences of B in A.
84 * Throws if the type is a bool.
85 */
86struct MinusOperator : public Modifier::BaseOperator
87{
Matt Spinlerc2727902022-02-02 11:13:09 -060088 MinusOperator(const json& valueObj) :
89 arg(ConfigBase::getJsonValue(valueObj))
90 {}
91
Matt Spinlerc09b8c82021-08-05 16:29:47 -050092 PropertyVariantType operator()(double val) override
93 {
94 return val - std::visit(ToTypeVisitor<double>(), arg);
95 }
96
97 PropertyVariantType operator()(int32_t val) override
98 {
99 return val - std::visit(ToTypeVisitor<int32_t>(), arg);
100 }
101
102 PropertyVariantType operator()(int64_t val) override
103 {
104 return val - std::visit(ToTypeVisitor<int64_t>(), arg);
105 }
106
107 PropertyVariantType operator()(const std::string& val) override
108 {
109 // Remove all occurrences of arg from val.
110 auto value = val;
111 auto toRemove = std::get<std::string>(arg);
112 size_t pos;
113 while ((pos = value.find(toRemove)) != std::string::npos)
114 {
115 value.erase(pos, toRemove.size());
116 }
117
118 return value;
119 }
120
121 PropertyVariantType operator()(bool val) override
122 {
123 throw std::runtime_error{
124 "Bool not allowed as a 'minus' modifier value"};
125 }
126
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500127 PropertyVariantType arg;
128};
129
Matt Spinlerc2727902022-02-02 11:13:09 -0600130/**
131 * @brief Implements an operator to return a value specified in
132 * the JSON that is chosen based on if the value passed
133 * into the operator is less than the lowest arg_value it
134 * is true for.
135 *
136 * "modifier": {
137 * "operator": "less_than",
138 * "value": [
139 * {
140 * "arg_value": 30, // if value is less than 30
141 * "parameter_value": 300 // then return 300
142 * },
143 * {
144 * "arg_value": 40, // else if value is less than 40
145 * "parameter_value": 400 // then return 400
146 * },
147 * ]
148 * }
149 *
150 * If the value passed in is higher than the highest arg_value,
151 * it returns a default value this is chosen based on the
152 * data type of parameter_value.
153 */
154struct LessThanOperator : public Modifier::BaseOperator
155{
156 LessThanOperator(const json& valueArray)
157 {
158 if (!valueArray.is_array())
159 {
160 log<level::ERR>(
161 fmt::format("Invalid JSON data for less_than config: {}",
162 valueArray.dump())
163 .c_str());
164 throw std::invalid_argument("Invalid modifier JSON");
165 }
166
167 for (const auto& valueEntry : valueArray)
168 {
169 if (!valueEntry.contains("arg_value") ||
170 !valueEntry.contains("parameter_value"))
171 {
172 log<level::ERR>(
173 fmt::format("Missing arg_value or parameter_value keys "
174 "in less_than config: {}",
175 valueArray.dump())
176 .c_str());
177 throw std::invalid_argument("Invalid modifier JSON");
178 }
179
180 auto argVal = ConfigBase::getJsonValue(valueEntry.at("arg_value"));
181
182 if (std::holds_alternative<bool>(argVal))
183 {
184 log<level::ERR>(
185 fmt::format(
186 "Invalid data type in arg_value key in modifier JSON "
187 "config: {}",
188 valueArray.dump())
189 .c_str());
190 throw std::invalid_argument("Invalid modifier JSON");
191 }
192
193 auto paramVal =
194 ConfigBase::getJsonValue(valueEntry.at("parameter_value"));
195
196 rangeValues.emplace_back(argVal, paramVal);
197 }
198
199 if (rangeValues.empty())
200 {
201 log<level::ERR>(fmt::format("No valid range values found in "
202 "modifier json: {}",
203 valueArray.dump())
204 .c_str());
205 throw std::invalid_argument("Invalid modifier JSON");
206 }
207 }
208
209 PropertyVariantType operator()(double val) override
210 {
211 for (const auto& rangeValue : rangeValues)
212 {
213 if (val < std::visit(ToTypeVisitor<double>(), rangeValue.first))
214 {
215 return rangeValue.second;
216 }
217 }
218 // Return a default value based on last entry type
219 return getDefaultValue(rangeValues.back().second);
220 }
221
222 PropertyVariantType operator()(int32_t val) override
223 {
224 for (const auto& rangeValue : rangeValues)
225 {
226 if (val < std::visit(ToTypeVisitor<int32_t>(), rangeValue.first))
227 {
228 return rangeValue.second;
229 }
230 }
231 return getDefaultValue(rangeValues.back().second);
232 }
233
234 PropertyVariantType operator()(int64_t val) override
235 {
236 for (const auto& rangeValue : rangeValues)
237 {
238 if (val < std::visit(ToTypeVisitor<int64_t>(), rangeValue.first))
239 {
240 return rangeValue.second;
241 }
242 }
243 return getDefaultValue(rangeValues.back().second);
244 }
245
246 PropertyVariantType operator()(const std::string& val) override
247 {
248 for (const auto& rangeValue : rangeValues)
249 {
250 if (val <
251 std::visit(ToTypeVisitor<std::string>(), rangeValue.first))
252 {
253 return rangeValue.second;
254 }
255 }
256 return getDefaultValue(rangeValues.back().second);
257 }
258
259 PropertyVariantType operator()(bool val) override
260 {
261 throw std::runtime_error{
262 "Bool not allowed as a 'less_than' modifier value"};
263 }
264
265 std::vector<std::pair<PropertyVariantType, PropertyVariantType>>
266 rangeValues;
267};
268
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500269Modifier::Modifier(const json& jsonObj)
270{
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500271 setOperator(jsonObj);
272}
273
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500274void Modifier::setOperator(const json& jsonObj)
275{
Matt Spinlerc2727902022-02-02 11:13:09 -0600276 if (!jsonObj.contains("operator") || !jsonObj.contains("value"))
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500277 {
278 log<level::ERR>(
Matt Spinlerc2727902022-02-02 11:13:09 -0600279 fmt::format(
280 "Modifier entry in JSON missing 'operator' or 'value': {}",
281 jsonObj.dump())
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500282 .c_str());
283 throw std::invalid_argument("Invalid modifier JSON");
284 }
285
286 auto op = jsonObj["operator"].get<std::string>();
287
288 if (op == "minus")
289 {
Matt Spinlerc2727902022-02-02 11:13:09 -0600290 _operator = std::make_unique<MinusOperator>(jsonObj["value"]);
291 }
292 else if (op == "less_than")
293 {
294 _operator = std::make_unique<LessThanOperator>(jsonObj["value"]);
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500295 }
296 else
297 {
298 log<level::ERR>(fmt::format("Invalid operator in the modifier JSON: {}",
299 jsonObj.dump())
300 .c_str());
301 throw std::invalid_argument("Invalid operator in the modifier JSON");
302 }
303}
304
305PropertyVariantType Modifier::doOp(const PropertyVariantType& val)
306{
307 return std::visit(*_operator, val);
308}
309
310} // namespace phosphor::fan::control::json