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
TargetFromGroupMax(const json & jsonObj,const std::vector<Group> & groups)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
run(Zone & zone)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 // negative hysteresis; or value is increasing and the change
58 // is greater than the positive hysteresis
59 if (((groupValue < _prevGroupValue) &&
60 (_prevGroupValue - groupValue > _negHysteresis)) ||
61 ((groupValue > _prevGroupValue) &&
62 (groupValue - _prevGroupValue > _posHysteresis)))
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
setHysteresis(const json & jsonObj)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
setIndex()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
setMap(const json & jsonObj)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
processGroups()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>(
194 std::format("{}: Group {}'s member "
195 "isn't numeric",
196 ActionBase::getName(), 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