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 // Value is decreasing and the change is greater than the 58 // positive hysteresis; or value is increasing and the change 59 // is greater than the positive hysteresis 60 if (((groupValue < _prevGroupValue) && 61 (_prevGroupValue - groupValue > _posHysteresis)) || 62 ((groupValue > _prevGroupValue) && 63 (groupValue - _prevGroupValue > _negHysteresis))) 64 { 65 /*The speed derived from mapping*/ 66 uint64_t groupSpeed = _speedFromGroupsMap[_groupIndex]; 67 68 // Looping through the mapping table 69 for (auto it = _valueToSpeedMap.begin(); 70 it != _valueToSpeedMap.end(); ++it) 71 { 72 // Value is at/below the first map key, or at/above the 73 // last map key, set speed to the first or the last map 74 // key's value respectively 75 if (((it == _valueToSpeedMap.begin()) && 76 (groupValue <= it->first)) || 77 ((std::next(it, 1) == _valueToSpeedMap.end()) && 78 (groupValue >= it->first))) 79 { 80 groupSpeed = it->second; 81 break; 82 } 83 // Value transitioned across a map key, update the speed 84 // to this map key's value when the new group value is at 85 // or above the map's key and below the next key 86 if (groupValue >= it->first && 87 groupValue < std::next(it, 1)->first) 88 { 89 groupSpeed = it->second; 90 break; 91 } 92 } 93 _prevGroupValue = groupValue; 94 _speedFromGroupsMap[_groupIndex] = groupSpeed; 95 } 96 } 97 // Get the maximum speed derived from all groups, and set target 98 // for the Zone 99 auto maxSpeedFromGroupsIter = std::max_element( 100 _speedFromGroupsMap.begin(), _speedFromGroupsMap.end(), 101 [](const auto& x, const auto& y) { return x.second < y.second; }); 102 103 zone.setTarget(maxSpeedFromGroupsIter->second); 104 } 105 else 106 { 107 // std::cerr << "Failed to process groups for " << ActionBase::getName() 108 // << ": Further processing will be skipped \n"; 109 } 110 } 111 112 void TargetFromGroupMax::setHysteresis(const json& jsonObj) 113 { 114 if (!jsonObj.contains("neg_hysteresis") || 115 !jsonObj.contains("pos_hysteresis")) 116 { 117 throw ActionParseError{ 118 ActionBase::getName(), 119 "Missing required neg_hysteresis or pos_hysteresis value"}; 120 } 121 _negHysteresis = jsonObj["neg_hysteresis"].get<uint64_t>(); 122 _posHysteresis = jsonObj["pos_hysteresis"].get<uint64_t>(); 123 } 124 125 void TargetFromGroupMax::setIndex() 126 { 127 _groupIndex = _groupIndexCounter; 128 // Initialize the map of each group and their max values 129 _speedFromGroupsMap[_groupIndex] = 0; 130 131 // Increase the index counter by one to specify the next group key 132 _groupIndexCounter += 1; 133 } 134 135 void TargetFromGroupMax::setMap(const json& jsonObj) 136 { 137 if (jsonObj.contains("map")) 138 { 139 for (const auto& map : jsonObj.at("map")) 140 { 141 142 if (!map.contains("value") || !map.contains("target")) 143 { 144 throw ActionParseError{ActionBase::getName(), 145 "Missing value or target in map"}; 146 } 147 else 148 { 149 uint64_t val = map["value"].get<uint64_t>(); 150 uint64_t target = map["target"].get<uint64_t>(); 151 _valueToSpeedMap.insert( 152 std::pair<uint64_t, uint64_t>(val, target)); 153 } 154 } 155 } 156 157 else 158 { 159 throw ActionParseError{ActionBase::getName(), "Missing required map"}; 160 } 161 } 162 163 std::optional<PropertyVariantType> TargetFromGroupMax::processGroups() 164 { 165 // Holds the max property value of groups 166 std::optional<PropertyVariantType> max; 167 168 for (const auto& group : _groups) 169 { 170 const auto& members = group.getMembers(); 171 for (const auto& member : members) 172 { 173 PropertyVariantType value; 174 bool invalid = false; 175 try 176 { 177 value = Manager::getObjValueVariant( 178 member, group.getInterface(), group.getProperty()); 179 } 180 catch (const std::out_of_range&) 181 { 182 continue; 183 } 184 185 // Only allow a group members to be 186 // numeric. Unlike with std::is_arithmetic, bools are not 187 // considered numeric here. 188 std::visit( 189 [&group, &invalid, this](auto&& val) { 190 using V = std::decay_t<decltype(val)>; 191 if constexpr (!std::is_same_v<double, V> && 192 !std::is_same_v<int32_t, V> && 193 !std::is_same_v<int64_t, V>) 194 { 195 log<level::ERR>(fmt::format("{}: Group {}'s member " 196 "isn't numeric", 197 ActionBase::getName(), 198 group.getName()) 199 .c_str()); 200 invalid = true; 201 } 202 }, 203 value); 204 if (invalid) 205 { 206 break; 207 } 208 209 if (max && (value > max)) 210 { 211 max = value; 212 } 213 else if (!max) 214 { 215 max = value; 216 } 217 } 218 } 219 return max; 220 } 221 222 } // namespace phosphor::fan::control::json 223