1 /** 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 19 #include "json/config_base.hpp" 20 #include "json/manager.hpp" 21 22 #include <fmt/format.h> 23 24 #include <phosphor-logging/log.hpp> 25 26 using namespace phosphor::logging; 27 28 namespace phosphor::fan::control::json 29 { 30 31 /** 32 * @brief Variant visitor to return a value of the template type specified. 33 */ 34 template <typename T> 35 struct 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 /** 50 * @brief Return a default value to use when the argument passed 51 * to LessThanOperator is out of range. 52 */ 53 PropertyVariantType 54 getDefaultValue(const PropertyVariantType& val, 55 const std::optional<PropertyVariantType>& defaultValue) 56 { 57 // When a default value is given, return that 58 if (defaultValue) 59 { 60 return *defaultValue; 61 } 62 63 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 /** 89 * @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 */ 94 struct MinusOperator : public Modifier::BaseOperator 95 { 96 MinusOperator(const json& jsonObj) : 97 arg(ConfigBase::getJsonValue(jsonObj["value"])) 98 {} 99 100 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 129 PropertyVariantType operator()(bool val) override 130 { 131 throw std::runtime_error{ 132 "Bool not allowed as a 'minus' modifier value"}; 133 } 134 135 PropertyVariantType arg; 136 }; 137 138 /** 139 * @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. 143 * 144 * "modifier": { 145 * "operator": "less_than", 146 * "default_value": 1000, // OPTIONAL 147 * "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 * 159 * 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. 162 */ 163 struct LessThanOperator : public Modifier::BaseOperator 164 { 165 LessThanOperator(const json& jsonObj) 166 { 167 const auto& valueArray = jsonObj["value"]; 168 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 } 217 218 if (jsonObj.contains("default_value")) 219 { 220 defaultValue = ConfigBase::getJsonValue(jsonObj["default_value"]); 221 } 222 } 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 234 return getDefaultValue(rangeValues.back().second, defaultValue); 235 } 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 } 246 return getDefaultValue(rangeValues.back().second, defaultValue); 247 } 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 } 258 return getDefaultValue(rangeValues.back().second, defaultValue); 259 } 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 } 271 return getDefaultValue(rangeValues.back().second, defaultValue); 272 } 273 274 PropertyVariantType operator()(bool val) override 275 { 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; 282 std::optional<PropertyVariantType> defaultValue; 283 }; 284 285 Modifier::Modifier(const json& jsonObj) 286 { 287 setOperator(jsonObj); 288 } 289 290 void Modifier::setOperator(const json& jsonObj) 291 { 292 if (!jsonObj.contains("operator") || !jsonObj.contains("value")) 293 { 294 log<level::ERR>( 295 fmt::format( 296 "Modifier entry in JSON missing 'operator' or 'value': {}", 297 jsonObj.dump()) 298 .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 { 306 _operator = std::make_unique<MinusOperator>(jsonObj); 307 } 308 else if (op == "less_than") 309 { 310 _operator = std::make_unique<LessThanOperator>(jsonObj); 311 } 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 321 PropertyVariantType Modifier::doOp(const PropertyVariantType& val) 322 { 323 return std::visit(*_operator, val); 324 } 325 326 } // namespace phosphor::fan::control::json 327