1 /** 2 * Copyright © 2022 Ampere Computing 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 #include "target_from_group_max.hpp" 17 18 #include "../manager.hpp" 19 20 #include <fmt/format.h> 21 22 #include <iostream> 23 24 namespace phosphor::fan::control::json 25 { 26 27 std::map<size_t, uint64_t> TargetFromGroupMax::_speedFromGroupsMap; 28 size_t TargetFromGroupMax::_groupIndexCounter = 0; 29 30 using json = nlohmann::json; 31 using namespace phosphor::logging; 32 33 TargetFromGroupMax::TargetFromGroupMax(const json& jsonObj, 34 const std::vector<Group>& groups) : 35 ActionBase(jsonObj, groups) 36 { 37 setHysteresis(jsonObj); 38 setMap(jsonObj); 39 setIndex(); 40 } 41 42 void TargetFromGroupMax::run(Zone& zone) 43 { 44 // Holds the max property value of groups 45 auto maxGroup = processGroups(); 46 47 // Group with non-numeric property value will be skipped from processing 48 if (maxGroup) 49 { 50 /*The maximum property value from the group*/ 51 uint64_t groupValue = 52 static_cast<uint64_t>(std::get<double>(maxGroup.value())); 53 54 // Only check if previous and new values differ 55 if (groupValue != _prevGroupValue) 56 { 57 /*The speed derived from mapping*/ 58 uint64_t groupSpeed = _speedFromGroupsMap[_groupIndex]; 59 60 // Value is decreasing from previous && greater than positive 61 // hysteresis 62 if ((groupValue < _prevGroupValue) && 63 (_prevGroupValue - groupValue > _posHysteresis)) 64 { 65 for (auto it = _valueToSpeedMap.rbegin(); 66 it != _valueToSpeedMap.rend(); ++it) 67 { 68 // Value is at/above last map key, set speed to the last map 69 // key's value 70 if (it == _valueToSpeedMap.rbegin() && 71 groupValue >= it->first) 72 { 73 groupSpeed = it->second; 74 break; 75 } 76 // Value is at/below first map key, set speed to the first 77 // map key's value 78 else if (std::next(it, 1) == _valueToSpeedMap.rend() && 79 groupValue <= it->first) 80 { 81 groupSpeed = it->second; 82 break; 83 } 84 // Value decreased & transitioned across a map key, update 85 // speed to this map key's value when new value is at or 86 // below map's key and the key is at/below the previous 87 // value 88 if (groupValue <= it->first && it->first <= _prevGroupValue) 89 { 90 groupSpeed = it->second; 91 } 92 } 93 _prevGroupValue = groupValue; 94 _speedFromGroupsMap[_groupIndex] = groupSpeed; 95 96 // Get the maximum speed derived from all groups, and set target 97 // for the Zone 98 auto maxSpeedFromGroupsIter = std::max_element( 99 _speedFromGroupsMap.begin(), _speedFromGroupsMap.end(), 100 [](const auto& x, const auto& y) { 101 return x.second < y.second; 102 }); 103 104 zone.setTarget(maxSpeedFromGroupsIter->second); 105 } 106 // Value is increasing from previous && greater than negative 107 // hysteresis 108 else 109 { 110 if (groupValue - _prevGroupValue > _negHysteresis) 111 { 112 for (auto it = _valueToSpeedMap.begin(); 113 it != _valueToSpeedMap.end(); ++it) 114 { 115 // Value is at/below the first map key, set speed to the 116 // first map key's value 117 if (it == _valueToSpeedMap.begin() && 118 groupValue <= it->first) 119 { 120 groupSpeed = it->second; 121 break; 122 } 123 // Value is at/above last map key, set speed to the last 124 // map key's value 125 else if (std::next(it, 1) == _valueToSpeedMap.end() && 126 groupValue >= it->first) 127 { 128 groupSpeed = it->second; 129 break; 130 } 131 // Value increased & transitioned across a map key, 132 // update speed to the next map key's value when new 133 // value is above map's key and the key is at/above the 134 // previous value 135 if (groupValue > it->first && 136 it->first >= _prevGroupValue) 137 { 138 groupSpeed = std::next(it, 1)->second; 139 } 140 // Value increased & transitioned across a map key, 141 // update speed to the map key's value when new value is 142 // at the map's key and the key is above the previous 143 // value 144 else if (groupValue == it->first && 145 it->first > _prevGroupValue) 146 { 147 groupSpeed = it->second; 148 } 149 } 150 } 151 _prevGroupValue = groupValue; 152 _speedFromGroupsMap[_groupIndex] = groupSpeed; 153 154 // Get the maximum speed derived from all groups, and set target 155 // for the Zone 156 auto maxSpeedFromGroupsIter = std::max_element( 157 _speedFromGroupsMap.begin(), _speedFromGroupsMap.end(), 158 [](const auto& x, const auto& y) { 159 return x.second < y.second; 160 }); 161 162 zone.setTarget(maxSpeedFromGroupsIter->second); 163 } 164 } 165 } 166 else 167 { 168 std::cerr << "Failed to process groups for " << ActionBase::getName() 169 << ": Further processing will be skipped \n"; 170 } 171 } 172 173 void TargetFromGroupMax::setHysteresis(const json& jsonObj) 174 { 175 if (!jsonObj.contains("neg_hysteresis") || 176 !jsonObj.contains("pos_hysteresis")) 177 { 178 throw ActionParseError{ 179 ActionBase::getName(), 180 "Missing required neg_hysteresis or pos_hysteresis value"}; 181 } 182 _negHysteresis = jsonObj["neg_hysteresis"].get<uint64_t>(); 183 _posHysteresis = jsonObj["pos_hysteresis"].get<uint64_t>(); 184 } 185 186 void TargetFromGroupMax::setIndex() 187 { 188 _groupIndex = _groupIndexCounter; 189 // Initialize the map of each group and their max values 190 _speedFromGroupsMap[_groupIndex] = 0; 191 192 // Increase the index counter by one to specify the next group key 193 _groupIndexCounter += 1; 194 } 195 196 void TargetFromGroupMax::setMap(const json& jsonObj) 197 { 198 if (jsonObj.contains("map")) 199 { 200 for (const auto& map : jsonObj.at("map")) 201 { 202 203 if (!map.contains("value") || !map.contains("target")) 204 { 205 throw ActionParseError{ActionBase::getName(), 206 "Missing value or target in map"}; 207 } 208 else 209 { 210 uint64_t val = map["value"].get<uint64_t>(); 211 uint64_t target = map["target"].get<uint64_t>(); 212 _valueToSpeedMap.insert( 213 std::pair<uint64_t, uint64_t>(val, target)); 214 } 215 } 216 } 217 218 else 219 { 220 throw ActionParseError{ActionBase::getName(), "Missing required map"}; 221 } 222 } 223 224 std::optional<PropertyVariantType> TargetFromGroupMax::processGroups() 225 { 226 // Holds the max property value of groups 227 std::optional<PropertyVariantType> max; 228 229 for (const auto& group : _groups) 230 { 231 const auto& members = group.getMembers(); 232 for (const auto& member : members) 233 { 234 PropertyVariantType value; 235 bool invalid = false; 236 try 237 { 238 value = Manager::getObjValueVariant( 239 member, group.getInterface(), group.getProperty()); 240 } 241 catch (const std::out_of_range&) 242 { 243 continue; 244 } 245 246 // Only allow a group members to be 247 // numeric. Unlike with std::is_arithmetic, bools are not 248 // considered numeric here. 249 std::visit( 250 [&group, &invalid, this](auto&& val) { 251 using V = std::decay_t<decltype(val)>; 252 if constexpr (!std::is_same_v<double, V> && 253 !std::is_same_v<int32_t, V> && 254 !std::is_same_v<int64_t, V>) 255 { 256 log<level::ERR>(fmt::format("{}: Group {}'s member " 257 "isn't numeric", 258 ActionBase::getName(), 259 group.getName()) 260 .c_str()); 261 invalid = true; 262 } 263 }, 264 value); 265 if (invalid) 266 { 267 break; 268 } 269 270 if (max && (value > max)) 271 { 272 max = value; 273 } 274 else if (!max) 275 { 276 max = value; 277 } 278 } 279 } 280 return max; 281 } 282 283 } // namespace phosphor::fan::control::json 284