1bb29bb7cSchaul.ampere /**
2bb29bb7cSchaul.ampere * Copyright © 2022 Ampere Computing
3bb29bb7cSchaul.ampere *
4bb29bb7cSchaul.ampere * Licensed under the Apache License, Version 2.0 (the "License");
5bb29bb7cSchaul.ampere * you may not use this file except in compliance with the License.
6bb29bb7cSchaul.ampere * You may obtain a copy of the License at
7bb29bb7cSchaul.ampere *
8bb29bb7cSchaul.ampere * http://www.apache.org/licenses/LICENSE-2.0
9bb29bb7cSchaul.ampere *
10bb29bb7cSchaul.ampere * Unless required by applicable law or agreed to in writing, software
11bb29bb7cSchaul.ampere * distributed under the License is distributed on an "AS IS" BASIS,
12bb29bb7cSchaul.ampere * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bb29bb7cSchaul.ampere * See the License for the specific language governing permissions and
14bb29bb7cSchaul.ampere * limitations under the License.
15bb29bb7cSchaul.ampere */
16bb29bb7cSchaul.ampere #include "target_from_group_max.hpp"
17bb29bb7cSchaul.ampere
18bb29bb7cSchaul.ampere #include "../manager.hpp"
19bb29bb7cSchaul.ampere
20*64b5ac20SAnwaar Hadi #include <phosphor-logging/lg2.hpp>
21*64b5ac20SAnwaar Hadi
22bb29bb7cSchaul.ampere #include <iostream>
23bb29bb7cSchaul.ampere
24bb29bb7cSchaul.ampere namespace phosphor::fan::control::json
25bb29bb7cSchaul.ampere {
26bb29bb7cSchaul.ampere
27bb29bb7cSchaul.ampere std::map<size_t, uint64_t> TargetFromGroupMax::_speedFromGroupsMap;
28bb29bb7cSchaul.ampere size_t TargetFromGroupMax::_groupIndexCounter = 0;
29bb29bb7cSchaul.ampere
30bb29bb7cSchaul.ampere using json = nlohmann::json;
31bb29bb7cSchaul.ampere
TargetFromGroupMax(const json & jsonObj,const std::vector<Group> & groups)32bb29bb7cSchaul.ampere TargetFromGroupMax::TargetFromGroupMax(const json& jsonObj,
33bb29bb7cSchaul.ampere const std::vector<Group>& groups) :
34bb29bb7cSchaul.ampere ActionBase(jsonObj, groups)
35bb29bb7cSchaul.ampere {
36bb29bb7cSchaul.ampere setHysteresis(jsonObj);
37bb29bb7cSchaul.ampere setMap(jsonObj);
38bb29bb7cSchaul.ampere setIndex();
39bb29bb7cSchaul.ampere }
40bb29bb7cSchaul.ampere
run(Zone & zone)41bb29bb7cSchaul.ampere void TargetFromGroupMax::run(Zone& zone)
42bb29bb7cSchaul.ampere {
43bb29bb7cSchaul.ampere // Holds the max property value of groups
44bb29bb7cSchaul.ampere auto maxGroup = processGroups();
45bb29bb7cSchaul.ampere
46bb29bb7cSchaul.ampere // Group with non-numeric property value will be skipped from processing
47bb29bb7cSchaul.ampere if (maxGroup)
48bb29bb7cSchaul.ampere {
49bb29bb7cSchaul.ampere /*The maximum property value from the group*/
50bb29bb7cSchaul.ampere uint64_t groupValue =
51bb29bb7cSchaul.ampere static_cast<uint64_t>(std::get<double>(maxGroup.value()));
52bb29bb7cSchaul.ampere
53bb29bb7cSchaul.ampere // Only check if previous and new values differ
54bb29bb7cSchaul.ampere if (groupValue != _prevGroupValue)
55bb29bb7cSchaul.ampere {
56b568962bSChau Ly // Value is decreasing and the change is greater than the
576781491cSChau Ly // negative hysteresis; or value is increasing and the change
58b568962bSChau Ly // is greater than the positive hysteresis
59b568962bSChau Ly if (((groupValue < _prevGroupValue) &&
606781491cSChau Ly (_prevGroupValue - groupValue > _negHysteresis)) ||
61b568962bSChau Ly ((groupValue > _prevGroupValue) &&
626781491cSChau Ly (groupValue - _prevGroupValue > _posHysteresis)))
63b568962bSChau Ly {
64bb29bb7cSchaul.ampere /*The speed derived from mapping*/
65bb29bb7cSchaul.ampere uint64_t groupSpeed = _speedFromGroupsMap[_groupIndex];
66bb29bb7cSchaul.ampere
67b568962bSChau Ly // Looping through the mapping table
68bb29bb7cSchaul.ampere for (auto it = _valueToSpeedMap.begin();
69bb29bb7cSchaul.ampere it != _valueToSpeedMap.end(); ++it)
70bb29bb7cSchaul.ampere {
71b568962bSChau Ly // Value is at/below the first map key, or at/above the
72b568962bSChau Ly // last map key, set speed to the first or the last map
73b568962bSChau Ly // key's value respectively
74b568962bSChau Ly if (((it == _valueToSpeedMap.begin()) &&
75b568962bSChau Ly (groupValue <= it->first)) ||
76b568962bSChau Ly ((std::next(it, 1) == _valueToSpeedMap.end()) &&
77b568962bSChau Ly (groupValue >= it->first)))
78bb29bb7cSchaul.ampere {
79bb29bb7cSchaul.ampere groupSpeed = it->second;
80bb29bb7cSchaul.ampere break;
81bb29bb7cSchaul.ampere }
82b568962bSChau Ly // Value transitioned across a map key, update the speed
83b568962bSChau Ly // to this map key's value when the new group value is at
84b568962bSChau Ly // or above the map's key and below the next key
85b568962bSChau Ly if (groupValue >= it->first &&
86b568962bSChau Ly groupValue < std::next(it, 1)->first)
87bb29bb7cSchaul.ampere {
88bb29bb7cSchaul.ampere groupSpeed = it->second;
89bb29bb7cSchaul.ampere break;
90bb29bb7cSchaul.ampere }
91bb29bb7cSchaul.ampere }
92bb29bb7cSchaul.ampere _prevGroupValue = groupValue;
93bb29bb7cSchaul.ampere _speedFromGroupsMap[_groupIndex] = groupSpeed;
94b568962bSChau Ly }
95b568962bSChau Ly }
96bb29bb7cSchaul.ampere // Get the maximum speed derived from all groups, and set target
97bb29bb7cSchaul.ampere // for the Zone
98bb29bb7cSchaul.ampere auto maxSpeedFromGroupsIter = std::max_element(
99bb29bb7cSchaul.ampere _speedFromGroupsMap.begin(), _speedFromGroupsMap.end(),
100b568962bSChau Ly [](const auto& x, const auto& y) { return x.second < y.second; });
101bb29bb7cSchaul.ampere
102bb29bb7cSchaul.ampere zone.setTarget(maxSpeedFromGroupsIter->second);
103bb29bb7cSchaul.ampere }
104bb29bb7cSchaul.ampere else
105bb29bb7cSchaul.ampere {
1060ee0504fSChau Ly // std::cerr << "Failed to process groups for " << ActionBase::getName()
1070ee0504fSChau Ly // << ": Further processing will be skipped \n";
108bb29bb7cSchaul.ampere }
109bb29bb7cSchaul.ampere }
110bb29bb7cSchaul.ampere
setHysteresis(const json & jsonObj)111bb29bb7cSchaul.ampere void TargetFromGroupMax::setHysteresis(const json& jsonObj)
112bb29bb7cSchaul.ampere {
113bb29bb7cSchaul.ampere if (!jsonObj.contains("neg_hysteresis") ||
114bb29bb7cSchaul.ampere !jsonObj.contains("pos_hysteresis"))
115bb29bb7cSchaul.ampere {
116bb29bb7cSchaul.ampere throw ActionParseError{
117bb29bb7cSchaul.ampere ActionBase::getName(),
118bb29bb7cSchaul.ampere "Missing required neg_hysteresis or pos_hysteresis value"};
119bb29bb7cSchaul.ampere }
120bb29bb7cSchaul.ampere _negHysteresis = jsonObj["neg_hysteresis"].get<uint64_t>();
121bb29bb7cSchaul.ampere _posHysteresis = jsonObj["pos_hysteresis"].get<uint64_t>();
122bb29bb7cSchaul.ampere }
123bb29bb7cSchaul.ampere
setIndex()124bb29bb7cSchaul.ampere void TargetFromGroupMax::setIndex()
125bb29bb7cSchaul.ampere {
126bb29bb7cSchaul.ampere _groupIndex = _groupIndexCounter;
127bb29bb7cSchaul.ampere // Initialize the map of each group and their max values
128bb29bb7cSchaul.ampere _speedFromGroupsMap[_groupIndex] = 0;
129bb29bb7cSchaul.ampere
130bb29bb7cSchaul.ampere // Increase the index counter by one to specify the next group key
131bb29bb7cSchaul.ampere _groupIndexCounter += 1;
132bb29bb7cSchaul.ampere }
133bb29bb7cSchaul.ampere
setMap(const json & jsonObj)134bb29bb7cSchaul.ampere void TargetFromGroupMax::setMap(const json& jsonObj)
135bb29bb7cSchaul.ampere {
136bb29bb7cSchaul.ampere if (jsonObj.contains("map"))
137bb29bb7cSchaul.ampere {
138bb29bb7cSchaul.ampere for (const auto& map : jsonObj.at("map"))
139bb29bb7cSchaul.ampere {
140bb29bb7cSchaul.ampere if (!map.contains("value") || !map.contains("target"))
141bb29bb7cSchaul.ampere {
142bb29bb7cSchaul.ampere throw ActionParseError{ActionBase::getName(),
143bb29bb7cSchaul.ampere "Missing value or target in map"};
144bb29bb7cSchaul.ampere }
145bb29bb7cSchaul.ampere else
146bb29bb7cSchaul.ampere {
147bb29bb7cSchaul.ampere uint64_t val = map["value"].get<uint64_t>();
148bb29bb7cSchaul.ampere uint64_t target = map["target"].get<uint64_t>();
149bb29bb7cSchaul.ampere _valueToSpeedMap.insert(
150bb29bb7cSchaul.ampere std::pair<uint64_t, uint64_t>(val, target));
151bb29bb7cSchaul.ampere }
152bb29bb7cSchaul.ampere }
153bb29bb7cSchaul.ampere }
154bb29bb7cSchaul.ampere
155bb29bb7cSchaul.ampere else
156bb29bb7cSchaul.ampere {
157bb29bb7cSchaul.ampere throw ActionParseError{ActionBase::getName(), "Missing required map"};
158bb29bb7cSchaul.ampere }
159bb29bb7cSchaul.ampere }
160bb29bb7cSchaul.ampere
processGroups()161bb29bb7cSchaul.ampere std::optional<PropertyVariantType> TargetFromGroupMax::processGroups()
162bb29bb7cSchaul.ampere {
163bb29bb7cSchaul.ampere // Holds the max property value of groups
164bb29bb7cSchaul.ampere std::optional<PropertyVariantType> max;
165bb29bb7cSchaul.ampere
166bb29bb7cSchaul.ampere for (const auto& group : _groups)
167bb29bb7cSchaul.ampere {
168bb29bb7cSchaul.ampere const auto& members = group.getMembers();
169bb29bb7cSchaul.ampere for (const auto& member : members)
170bb29bb7cSchaul.ampere {
171bb29bb7cSchaul.ampere PropertyVariantType value;
172bb29bb7cSchaul.ampere bool invalid = false;
173bb29bb7cSchaul.ampere try
174bb29bb7cSchaul.ampere {
175bb29bb7cSchaul.ampere value = Manager::getObjValueVariant(
176bb29bb7cSchaul.ampere member, group.getInterface(), group.getProperty());
177bb29bb7cSchaul.ampere }
178bb29bb7cSchaul.ampere catch (const std::out_of_range&)
179bb29bb7cSchaul.ampere {
180bb29bb7cSchaul.ampere continue;
181bb29bb7cSchaul.ampere }
182bb29bb7cSchaul.ampere
183bb29bb7cSchaul.ampere // Only allow a group members to be
184bb29bb7cSchaul.ampere // numeric. Unlike with std::is_arithmetic, bools are not
185bb29bb7cSchaul.ampere // considered numeric here.
186bb29bb7cSchaul.ampere std::visit(
187bb29bb7cSchaul.ampere [&group, &invalid, this](auto&& val) {
188bb29bb7cSchaul.ampere using V = std::decay_t<decltype(val)>;
189bb29bb7cSchaul.ampere if constexpr (!std::is_same_v<double, V> &&
190bb29bb7cSchaul.ampere !std::is_same_v<int32_t, V> &&
191bb29bb7cSchaul.ampere !std::is_same_v<int64_t, V>)
192bb29bb7cSchaul.ampere {
193*64b5ac20SAnwaar Hadi lg2::error("{ACTION_NAME}: Group {GROUP_NAME}'s member "
194bb29bb7cSchaul.ampere "isn't numeric",
195*64b5ac20SAnwaar Hadi "ACTION_NAME", ActionBase::getName(),
196*64b5ac20SAnwaar Hadi "GROUP_NAME", group.getName());
197bb29bb7cSchaul.ampere invalid = true;
198bb29bb7cSchaul.ampere }
199bb29bb7cSchaul.ampere },
200bb29bb7cSchaul.ampere value);
201bb29bb7cSchaul.ampere if (invalid)
202bb29bb7cSchaul.ampere {
203bb29bb7cSchaul.ampere break;
204bb29bb7cSchaul.ampere }
205bb29bb7cSchaul.ampere
206bb29bb7cSchaul.ampere if (max && (value > max))
207bb29bb7cSchaul.ampere {
208bb29bb7cSchaul.ampere max = value;
209bb29bb7cSchaul.ampere }
210bb29bb7cSchaul.ampere else if (!max)
211bb29bb7cSchaul.ampere {
212bb29bb7cSchaul.ampere max = value;
213bb29bb7cSchaul.ampere }
214bb29bb7cSchaul.ampere }
215bb29bb7cSchaul.ampere }
216bb29bb7cSchaul.ampere return max;
217bb29bb7cSchaul.ampere }
218bb29bb7cSchaul.ampere
219bb29bb7cSchaul.ampere } // namespace phosphor::fan::control::json
220