1 /**
2  * Copyright © 2022 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 "override_fan_target.hpp"
17 
18 #include "../manager.hpp"
19 #include "../zone.hpp"
20 #include "action.hpp"
21 #include "group.hpp"
22 
23 #include <nlohmann/json.hpp>
24 
25 #include <format>
26 
27 namespace phosphor::fan::control::json
28 {
29 
30 using json = nlohmann::json;
31 
32 OverrideFanTarget::OverrideFanTarget(const json& jsonObj,
33                                      const std::vector<Group>& groups) :
34     ActionBase(jsonObj, groups)
35 {
36     setCount(jsonObj);
37     setState(jsonObj);
38     setTarget(jsonObj);
39     setFans(jsonObj);
40 }
41 
42 void OverrideFanTarget::run(Zone& zone)
43 {
44     size_t numAtState = 0;
45 
46     for (const auto& group : _groups)
47     {
48         for (const auto& member : group.getMembers())
49         {
50             try
51             {
52                 if (Manager::getObjValueVariant(member, group.getInterface(),
53                                                 group.getProperty()) == _state)
54                 {
55                     numAtState++;
56 
57                     if (numAtState >= _count)
58                     {
59                         break;
60                     }
61                 }
62             }
63             catch (const std::out_of_range&)
64             {}
65         }
66 
67         // lock the fans
68         if (numAtState >= _count)
69         {
70             lockFans(zone);
71             break;
72         }
73     }
74 
75     if (_locked && numAtState < _count)
76     {
77         unlockFans(zone);
78     }
79 }
80 
81 void OverrideFanTarget::lockFans(Zone& zone)
82 {
83     if (!_locked)
84     {
85         std::string fanList;
86         if (!_fans.empty())
87         {
88             fanList = std::accumulate(std::next(_fans.begin()), _fans.end(),
89                                       _fans[0], [](auto list, auto fan) {
90                 return std::move(list) + ", " + fan;
91             });
92         }
93 
94         record(std::format("Adding fan target lock of {} on fans [{}] zone {}",
95                            _target, fanList, zone.getName()));
96 
97         for (auto& fan : _fans)
98         {
99             zone.lockFanTarget(fan, _target);
100         }
101 
102         _locked = true;
103     }
104 }
105 
106 void OverrideFanTarget::unlockFans(Zone& zone)
107 {
108     std::string fanList;
109     if (!_fans.empty())
110     {
111         fanList = std::accumulate(
112             std::next(_fans.begin()), _fans.end(), _fans[0],
113             [](auto list, auto fan) { return std::move(list) + ", " + fan; });
114     }
115 
116     record(std::format("Un-locking fan target {} on fans [{}] zone {}", _target,
117                        fanList, zone.getName()));
118 
119     // unlock all fans in this instance
120     for (auto& fan : _fans)
121     {
122         zone.unlockFanTarget(fan, _target);
123     }
124 
125     _locked = false;
126 }
127 
128 void OverrideFanTarget::setCount(const json& jsonObj)
129 {
130     if (!jsonObj.contains("count"))
131     {
132         throw ActionParseError{ActionBase::getName(),
133                                "Missing required count value"};
134     }
135     _count = jsonObj["count"].get<size_t>();
136 }
137 
138 void OverrideFanTarget::setState(const json& jsonObj)
139 {
140     if (!jsonObj.contains("state"))
141     {
142         throw ActionParseError{ActionBase::getName(),
143                                "Missing required state value"};
144     }
145     _state = getJsonValue(jsonObj["state"]);
146 }
147 
148 void OverrideFanTarget::setTarget(const json& jsonObj)
149 {
150     if (!jsonObj.contains("target"))
151     {
152         throw ActionParseError{ActionBase::getName(),
153                                "Missing required target value"};
154     }
155     _target = jsonObj["target"].get<uint64_t>();
156 }
157 
158 void OverrideFanTarget::setFans(const json& jsonObj)
159 {
160     if (!jsonObj.contains("fans"))
161     {
162         throw ActionParseError{ActionBase::getName(),
163                                "Missing required fans value"};
164     }
165 
166     _fans = jsonObj["fans"].get<std::vector<std::string>>();
167 }
168 
169 } // namespace phosphor::fan::control::json
170