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