1 /**
2  * Copyright © 2021 IBM Corporation
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 "set_parameter_from_group_max.hpp"
17 
18 #include "../manager.hpp"
19 
20 #include <format>
21 
22 namespace phosphor::fan::control::json
23 {
24 
25 using json = nlohmann::json;
26 using namespace phosphor::logging;
27 
SetParameterFromGroupMax(const json & jsonObj,const std::vector<Group> & groups)28 SetParameterFromGroupMax::SetParameterFromGroupMax(
29     const json& jsonObj, const std::vector<Group>& groups) :
30     ActionBase(jsonObj, groups)
31 {
32     setParameterName(jsonObj);
33     setModifier(jsonObj);
34 }
35 
run(Zone &)36 void SetParameterFromGroupMax::run(Zone& /*zone*/)
37 {
38     std::optional<PropertyVariantType> max;
39 
40     // Find the maximum value of all group member properties, possibly modify
41     // it, and then write it to the Manager as a parameter.
42 
43     for (const auto& group : _groups)
44     {
45         const auto& members = group.getMembers();
46         for (const auto& member : members)
47         {
48             PropertyVariantType value;
49             try
50             {
51                 value = Manager::getObjValueVariant(
52                     member, group.getInterface(), group.getProperty());
53             }
54             catch (const std::out_of_range&)
55             {
56                 continue;
57             }
58 
59             // Only allow a group to have multiple members if it's
60             // numeric. Unlike with std::is_arithmetic, bools are not
61             // considered numeric here.
62             if (members.size() > 1)
63             {
64                 bool invalid = false;
65                 std::visit(
66                     [&group, &invalid, this](auto&& val) {
67                     using V = std::decay_t<decltype(val)>;
68                     if constexpr (!std::is_same_v<double, V> &&
69                                   !std::is_same_v<int32_t, V> &&
70                                   !std::is_same_v<int64_t, V>)
71                     {
72                         log<level::ERR>(std::format("{}: Group {} has more "
73                                                     "than one member but "
74                                                     "isn't numeric",
75                                                     ActionBase::getName(),
76                                                     group.getName())
77                                             .c_str());
78                         invalid = true;
79                     }
80                 },
81                     value);
82                 if (invalid)
83                 {
84                     continue;
85                 }
86             }
87 
88             if (max && (value > max))
89             {
90                 max = value;
91             }
92             else if (!max)
93             {
94                 max = value;
95             }
96         }
97     }
98 
99     if (_modifier && max)
100     {
101         try
102         {
103             *max = _modifier->doOp(*max);
104         }
105         catch (const std::exception& e)
106         {
107             log<level::ERR>(
108                 std::format("{}: Could not perform modifier operation: {}",
109                             ActionBase::getName(), e.what())
110                     .c_str());
111             return;
112         }
113     }
114 
115     Manager::setParameter(_name, max);
116 }
117 
setParameterName(const json & jsonObj)118 void SetParameterFromGroupMax::setParameterName(const json& jsonObj)
119 {
120     if (!jsonObj.contains("parameter_name"))
121     {
122         throw ActionParseError{ActionBase::getName(),
123                                "Missing required parameter_name value"};
124     }
125 
126     _name = jsonObj["parameter_name"].get<std::string>();
127 }
128 
setModifier(const json & jsonObj)129 void SetParameterFromGroupMax::setModifier(const json& jsonObj)
130 {
131     if (jsonObj.contains("modifier"))
132     {
133         try
134         {
135             _modifier = std::make_unique<Modifier>(jsonObj.at("modifier"));
136         }
137         catch (const std::invalid_argument& e)
138         {
139             throw ActionParseError{ActionBase::getName(), e.what()};
140         }
141     }
142 }
143 
144 }; // namespace phosphor::fan::control::json
145