blob: c19aca1abd896b974fd2189725efcf32c7d9fd23 [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
Anwaar Hadi64b5ac22025-04-04 23:54:53 +000022#include <phosphor-logging/lg2.hpp>
Matt Spinlerc09b8c82021-08-05 16:29:47 -050023
24namespace phosphor::fan::control::json
25{
26
27/**
28 * @brief Variant visitor to return a value of the template type specified.
29 */
30template <typename T>
31struct ToTypeVisitor
32{
33 template <typename U>
34 T operator()(const U& t) const
35 {
36 if constexpr (std::is_arithmetic_v<U> && std::is_arithmetic_v<T>)
37 {
38 return static_cast<T>(t);
39 }
40 throw std::invalid_argument(
41 "Non arithmetic type used in ToTypeVisitor");
42 }
43};
44
45/**
Matt Spinlerc2727902022-02-02 11:13:09 -060046 * @brief Return a default value to use when the argument passed
47 * to LessThanOperator is out of range.
48 */
Patrick Williams4fa67aa2025-02-03 14:28:47 -050049PropertyVariantType getDefaultValue(
50 const PropertyVariantType& val,
51 const std::optional<PropertyVariantType>& defaultValue)
Matt Spinlerc2727902022-02-02 11:13:09 -060052{
Matthew Barth6941fd72022-03-10 16:50:30 -060053 // When a default value is given, return that
54 if (defaultValue)
55 {
56 return *defaultValue;
57 }
58
Matt Spinlerc2727902022-02-02 11:13:09 -060059 if (std::holds_alternative<bool>(val))
60 {
61 return false;
62 }
63 else if (std::holds_alternative<std::string>(val))
64 {
65 return std::string{};
66 }
67 else if (std::holds_alternative<double>(val))
68 {
69 return std::numeric_limits<double>::quiet_NaN();
70 }
71 else if (std::holds_alternative<int32_t>(val))
72 {
73 return std::numeric_limits<int32_t>::quiet_NaN();
74 }
75 else if (std::holds_alternative<int64_t>(val))
76 {
77 return std::numeric_limits<int64_t>::quiet_NaN();
78 }
79
80 throw std::runtime_error(
81 "Invalid variant type when determining default value");
82}
83
84/**
Matt Spinlerc09b8c82021-08-05 16:29:47 -050085 * @brief Implements the minus operator to subtract two values.
86 *
87 * With strings values, A - B removes all occurrences of B in A.
88 * Throws if the type is a bool.
89 */
90struct MinusOperator : public Modifier::BaseOperator
91{
Matt Spinler9b062432023-01-26 14:38:50 -060092 explicit MinusOperator(const json& jsonObj) :
Matthew Barth6941fd72022-03-10 16:50:30 -060093 arg(ConfigBase::getJsonValue(jsonObj["value"]))
Matt Spinlerc2727902022-02-02 11:13:09 -060094 {}
95
Matt Spinlerc09b8c82021-08-05 16:29:47 -050096 PropertyVariantType operator()(double val) override
97 {
98 return val - std::visit(ToTypeVisitor<double>(), arg);
99 }
100
101 PropertyVariantType operator()(int32_t val) override
102 {
103 return val - std::visit(ToTypeVisitor<int32_t>(), arg);
104 }
105
106 PropertyVariantType operator()(int64_t val) override
107 {
108 return val - std::visit(ToTypeVisitor<int64_t>(), arg);
109 }
110
111 PropertyVariantType operator()(const std::string& val) override
112 {
113 // Remove all occurrences of arg from val.
114 auto value = val;
115 auto toRemove = std::get<std::string>(arg);
116 size_t pos;
117 while ((pos = value.find(toRemove)) != std::string::npos)
118 {
119 value.erase(pos, toRemove.size());
120 }
121
122 return value;
123 }
124
Mike Cappsb2e9a4f2022-06-13 10:15:42 -0400125 PropertyVariantType operator()(bool) override
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500126 {
127 throw std::runtime_error{
128 "Bool not allowed as a 'minus' modifier value"};
129 }
130
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500131 PropertyVariantType arg;
132};
133
Matt Spinlerc2727902022-02-02 11:13:09 -0600134/**
Matthew Barth6941fd72022-03-10 16:50:30 -0600135 * @brief Implements an operator to return a value specified in the JSON that is
136 * chosen based on if the value passed into the operator is less than the lowest
137 * arg_value it is true for or the given `default_value` if not found to be less
138 * than any entries.
Matt Spinlerc2727902022-02-02 11:13:09 -0600139 *
140 * "modifier": {
141 * "operator": "less_than",
Matthew Barth6941fd72022-03-10 16:50:30 -0600142 * "default_value": 1000, // OPTIONAL
Matt Spinlerc2727902022-02-02 11:13:09 -0600143 * "value": [
144 * {
145 * "arg_value": 30, // if value is less than 30
146 * "parameter_value": 300 // then return 300
147 * },
148 * {
149 * "arg_value": 40, // else if value is less than 40
150 * "parameter_value": 400 // then return 400
151 * },
152 * ]
153 * }
154 *
Matthew Barth6941fd72022-03-10 16:50:30 -0600155 * If the value passed in is higher than the highest arg_value, it returns a
156 * default value this is the `default_value` given or based on the data type of
157 * parameter_value.
Matt Spinlerc2727902022-02-02 11:13:09 -0600158 */
159struct LessThanOperator : public Modifier::BaseOperator
160{
Matt Spinler9b062432023-01-26 14:38:50 -0600161 explicit LessThanOperator(const json& jsonObj)
Matt Spinlerc2727902022-02-02 11:13:09 -0600162 {
Matthew Barth6941fd72022-03-10 16:50:30 -0600163 const auto& valueArray = jsonObj["value"];
Matt Spinlerc2727902022-02-02 11:13:09 -0600164 if (!valueArray.is_array())
165 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000166 lg2::error("Invalid JSON data for less_than config: {VALUE_ARRAY}",
167 "VALUE_ARRAY", valueArray.dump());
Matt Spinlerc2727902022-02-02 11:13:09 -0600168 throw std::invalid_argument("Invalid modifier JSON");
169 }
170
171 for (const auto& valueEntry : valueArray)
172 {
173 if (!valueEntry.contains("arg_value") ||
174 !valueEntry.contains("parameter_value"))
175 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000176 lg2::error("Missing arg_value or parameter_value keys "
177 "in less_than config: {VALUE_ARRAY}",
178 "VALUE_ARRAY", valueArray.dump());
Matt Spinlerc2727902022-02-02 11:13:09 -0600179 throw std::invalid_argument("Invalid modifier JSON");
180 }
181
182 auto argVal = ConfigBase::getJsonValue(valueEntry.at("arg_value"));
183
184 if (std::holds_alternative<bool>(argVal))
185 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000186 lg2::error(
187 "Invalid data type in arg_value key in modifier JSON "
188 "config: {VALUE_ARRAY}",
189 "VALUE_ARRAY", valueArray.dump());
Matt Spinlerc2727902022-02-02 11:13:09 -0600190 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 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000201 lg2::error("No valid range values found in "
202 "modifier json: {VALUE_ARRAY}",
203 "VALUE_ARRAY", valueArray.dump());
Matt Spinlerc2727902022-02-02 11:13:09 -0600204 throw std::invalid_argument("Invalid modifier JSON");
205 }
Matthew Barth6941fd72022-03-10 16:50:30 -0600206
207 if (jsonObj.contains("default_value"))
208 {
209 defaultValue = ConfigBase::getJsonValue(jsonObj["default_value"]);
210 }
Matt Spinlerc2727902022-02-02 11:13:09 -0600211 }
212
213 PropertyVariantType operator()(double val) override
214 {
215 for (const auto& rangeValue : rangeValues)
216 {
217 if (val < std::visit(ToTypeVisitor<double>(), rangeValue.first))
218 {
219 return rangeValue.second;
220 }
221 }
222 // Return a default value based on last entry type
Matthew Barth6941fd72022-03-10 16:50:30 -0600223 return getDefaultValue(rangeValues.back().second, defaultValue);
Matt Spinlerc2727902022-02-02 11:13:09 -0600224 }
225
226 PropertyVariantType operator()(int32_t val) override
227 {
228 for (const auto& rangeValue : rangeValues)
229 {
230 if (val < std::visit(ToTypeVisitor<int32_t>(), rangeValue.first))
231 {
232 return rangeValue.second;
233 }
234 }
Matthew Barth6941fd72022-03-10 16:50:30 -0600235 return getDefaultValue(rangeValues.back().second, defaultValue);
Matt Spinlerc2727902022-02-02 11:13:09 -0600236 }
237
238 PropertyVariantType operator()(int64_t val) override
239 {
240 for (const auto& rangeValue : rangeValues)
241 {
242 if (val < std::visit(ToTypeVisitor<int64_t>(), rangeValue.first))
243 {
244 return rangeValue.second;
245 }
246 }
Matthew Barth6941fd72022-03-10 16:50:30 -0600247 return getDefaultValue(rangeValues.back().second, defaultValue);
Matt Spinlerc2727902022-02-02 11:13:09 -0600248 }
249
250 PropertyVariantType operator()(const std::string& val) override
251 {
252 for (const auto& rangeValue : rangeValues)
253 {
254 if (val <
255 std::visit(ToTypeVisitor<std::string>(), rangeValue.first))
256 {
257 return rangeValue.second;
258 }
259 }
Matthew Barth6941fd72022-03-10 16:50:30 -0600260 return getDefaultValue(rangeValues.back().second, defaultValue);
Matt Spinlerc2727902022-02-02 11:13:09 -0600261 }
262
Mike Cappsb2e9a4f2022-06-13 10:15:42 -0400263 PropertyVariantType operator()(bool) override
Matt Spinlerc2727902022-02-02 11:13:09 -0600264 {
265 throw std::runtime_error{
266 "Bool not allowed as a 'less_than' modifier value"};
267 }
268
269 std::vector<std::pair<PropertyVariantType, PropertyVariantType>>
270 rangeValues;
Matthew Barth6941fd72022-03-10 16:50:30 -0600271 std::optional<PropertyVariantType> defaultValue;
Matt Spinlerc2727902022-02-02 11:13:09 -0600272};
273
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500274Modifier::Modifier(const json& jsonObj)
275{
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500276 setOperator(jsonObj);
277}
278
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500279void Modifier::setOperator(const json& jsonObj)
280{
Matt Spinlerc2727902022-02-02 11:13:09 -0600281 if (!jsonObj.contains("operator") || !jsonObj.contains("value"))
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500282 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000283 lg2::error(
284 "Modifier entry in JSON missing 'operator' or 'value': {JSON_OBJECT}",
285 "JSON_OBJECT", jsonObj.dump());
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500286 throw std::invalid_argument("Invalid modifier JSON");
287 }
288
289 auto op = jsonObj["operator"].get<std::string>();
290
291 if (op == "minus")
292 {
Matthew Barth6941fd72022-03-10 16:50:30 -0600293 _operator = std::make_unique<MinusOperator>(jsonObj);
Matt Spinlerc2727902022-02-02 11:13:09 -0600294 }
295 else if (op == "less_than")
296 {
Matthew Barth6941fd72022-03-10 16:50:30 -0600297 _operator = std::make_unique<LessThanOperator>(jsonObj);
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500298 }
299 else
300 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000301 lg2::error("Invalid operator in the modifier JSON: {JSON_OBJECT}",
302 "JSON_OBJECT", jsonObj.dump());
Matt Spinlerc09b8c82021-08-05 16:29:47 -0500303 throw std::invalid_argument("Invalid operator in the modifier JSON");
304 }
305}
306
307PropertyVariantType Modifier::doOp(const PropertyVariantType& val)
308{
309 return std::visit(*_operator, val);
310}
311
312} // namespace phosphor::fan::control::json