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 if (!map.contains("value") || !map.contains("target")) 142 { 143 throw ActionParseError{ActionBase::getName(), 144 "Missing value or target in map"}; 145 } 146 else 147 { 148 uint64_t val = map["value"].get<uint64_t>(); 149 uint64_t target = map["target"].get<uint64_t>(); 150 _valueToSpeedMap.insert( 151 std::pair<uint64_t, uint64_t>(val, target)); 152 } 153 } 154 } 155 156 else 157 { 158 throw ActionParseError{ActionBase::getName(), "Missing required map"}; 159 } 160 } 161 162 std::optional<PropertyVariantType> TargetFromGroupMax::processGroups() 163 { 164 // Holds the max property value of groups 165 std::optional<PropertyVariantType> max; 166 167 for (const auto& group : _groups) 168 { 169 const auto& members = group.getMembers(); 170 for (const auto& member : members) 171 { 172 PropertyVariantType value; 173 bool invalid = false; 174 try 175 { 176 value = Manager::getObjValueVariant( 177 member, group.getInterface(), group.getProperty()); 178 } 179 catch (const std::out_of_range&) 180 { 181 continue; 182 } 183 184 // Only allow a group members to be 185 // numeric. Unlike with std::is_arithmetic, bools are not 186 // considered numeric here. 187 std::visit( 188 [&group, &invalid, this](auto&& val) { 189 using V = std::decay_t<decltype(val)>; 190 if constexpr (!std::is_same_v<double, V> && 191 !std::is_same_v<int32_t, V> && 192 !std::is_same_v<int64_t, V>) 193 { 194 log<level::ERR>(fmt::format("{}: Group {}'s member " 195 "isn't numeric", 196 ActionBase::getName(), 197 group.getName()) 198 .c_str()); 199 invalid = true; 200 } 201 }, 202 value); 203 if (invalid) 204 { 205 break; 206 } 207 208 if (max && (value > max)) 209 { 210 max = value; 211 } 212 else if (!max) 213 { 214 max = value; 215 } 216 } 217 } 218 return max; 219 } 220 221 } // namespace phosphor::fan::control::json 222