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 if ((groupValue > _prevGroupValue) && 109 (groupValue - _prevGroupValue > _negHysteresis)) 110 { 111 for (auto it = _valueToSpeedMap.begin(); 112 it != _valueToSpeedMap.end(); ++it) 113 { 114 // Value is at/below the first map key, set speed to the 115 // first map key's value 116 if (it == _valueToSpeedMap.begin() && 117 groupValue <= it->first) 118 { 119 groupSpeed = it->second; 120 break; 121 } 122 // Value is at/above last map key, set speed to the last 123 // map key's value 124 else if (std::next(it, 1) == _valueToSpeedMap.end() && 125 groupValue >= it->first) 126 { 127 groupSpeed = it->second; 128 break; 129 } 130 // Value increased & transitioned across a map key, 131 // update speed to the next map key's value when new 132 // value is above map's key and the key is at/above the 133 // previous value 134 if (groupValue > it->first && it->first >= _prevGroupValue) 135 { 136 groupSpeed = std::next(it, 1)->second; 137 } 138 // Value increased & transitioned across a map key, 139 // update speed to the map key's value when new value is 140 // at the map's key and the key is above the previous 141 // value 142 else if (groupValue == it->first && 143 it->first > _prevGroupValue) 144 { 145 groupSpeed = it->second; 146 } 147 } 148 _prevGroupValue = groupValue; 149 _speedFromGroupsMap[_groupIndex] = groupSpeed; 150 151 // Get the maximum speed derived from all groups, and set target 152 // for the Zone 153 auto maxSpeedFromGroupsIter = std::max_element( 154 _speedFromGroupsMap.begin(), _speedFromGroupsMap.end(), 155 [](const auto& x, const auto& y) { 156 return x.second < y.second; 157 }); 158 159 zone.setTarget(maxSpeedFromGroupsIter->second); 160 } 161 } 162 } 163 else 164 { 165 // std::cerr << "Failed to process groups for " << ActionBase::getName() 166 // << ": Further processing will be skipped \n"; 167 } 168 } 169 170 void TargetFromGroupMax::setHysteresis(const json& jsonObj) 171 { 172 if (!jsonObj.contains("neg_hysteresis") || 173 !jsonObj.contains("pos_hysteresis")) 174 { 175 throw ActionParseError{ 176 ActionBase::getName(), 177 "Missing required neg_hysteresis or pos_hysteresis value"}; 178 } 179 _negHysteresis = jsonObj["neg_hysteresis"].get<uint64_t>(); 180 _posHysteresis = jsonObj["pos_hysteresis"].get<uint64_t>(); 181 } 182 183 void TargetFromGroupMax::setIndex() 184 { 185 _groupIndex = _groupIndexCounter; 186 // Initialize the map of each group and their max values 187 _speedFromGroupsMap[_groupIndex] = 0; 188 189 // Increase the index counter by one to specify the next group key 190 _groupIndexCounter += 1; 191 } 192 193 void TargetFromGroupMax::setMap(const json& jsonObj) 194 { 195 if (jsonObj.contains("map")) 196 { 197 for (const auto& map : jsonObj.at("map")) 198 { 199 200 if (!map.contains("value") || !map.contains("target")) 201 { 202 throw ActionParseError{ActionBase::getName(), 203 "Missing value or target in map"}; 204 } 205 else 206 { 207 uint64_t val = map["value"].get<uint64_t>(); 208 uint64_t target = map["target"].get<uint64_t>(); 209 _valueToSpeedMap.insert( 210 std::pair<uint64_t, uint64_t>(val, target)); 211 } 212 } 213 } 214 215 else 216 { 217 throw ActionParseError{ActionBase::getName(), "Missing required map"}; 218 } 219 } 220 221 std::optional<PropertyVariantType> TargetFromGroupMax::processGroups() 222 { 223 // Holds the max property value of groups 224 std::optional<PropertyVariantType> max; 225 226 for (const auto& group : _groups) 227 { 228 const auto& members = group.getMembers(); 229 for (const auto& member : members) 230 { 231 PropertyVariantType value; 232 bool invalid = false; 233 try 234 { 235 value = Manager::getObjValueVariant( 236 member, group.getInterface(), group.getProperty()); 237 } 238 catch (const std::out_of_range&) 239 { 240 continue; 241 } 242 243 // Only allow a group members to be 244 // numeric. Unlike with std::is_arithmetic, bools are not 245 // considered numeric here. 246 std::visit( 247 [&group, &invalid, this](auto&& val) { 248 using V = std::decay_t<decltype(val)>; 249 if constexpr (!std::is_same_v<double, V> && 250 !std::is_same_v<int32_t, V> && 251 !std::is_same_v<int64_t, V>) 252 { 253 log<level::ERR>(fmt::format("{}: Group {}'s member " 254 "isn't numeric", 255 ActionBase::getName(), 256 group.getName()) 257 .c_str()); 258 invalid = true; 259 } 260 }, 261 value); 262 if (invalid) 263 { 264 break; 265 } 266 267 if (max && (value > max)) 268 { 269 max = value; 270 } 271 else if (!max) 272 { 273 max = value; 274 } 275 } 276 } 277 return max; 278 } 279 280 } // namespace phosphor::fan::control::json 281