xref: /openbmc/phosphor-fan-presence/control/json/actions/target_from_group_max.cpp (revision 64b5ac203518568ec8b7569d0e785352278f2472)
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