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 "timer_based_actions.hpp"
17
18 #include "../manager.hpp"
19 #include "action.hpp"
20 #include "event.hpp"
21 #include "group.hpp"
22 #include "sdbusplus.hpp"
23 #include "sdeventplus.hpp"
24 #include "zone.hpp"
25
26 #include <nlohmann/json.hpp>
27
28 #include <algorithm>
29 #include <chrono>
30 #include <format>
31
32 namespace phosphor::fan::control::json
33 {
34
35 using json = nlohmann::json;
36
TimerBasedActions(const json & jsonObj,const std::vector<Group> & groups)37 TimerBasedActions::TimerBasedActions(const json& jsonObj,
38 const std::vector<Group>& groups) :
39 ActionBase(jsonObj, groups),
40 _timer(util::SDEventPlus::getEvent(),
41 std::bind(&TimerBasedActions::timerExpired, this))
42 {
43 // If any of groups' value == nullopt(i.e. not configured), action is
44 // driven by the service owned state of the group members
45 _byOwner =
46 std::any_of(_groups.begin(), _groups.end(), [](const auto& group) {
47 return group.getValue() == std::nullopt;
48 });
49
50 setTimerConf(jsonObj);
51 setActions(jsonObj);
52 }
53
run(Zone & zone)54 void TimerBasedActions::run(Zone& zone)
55 {
56 if (_byOwner)
57 {
58 // If any service providing a group member is not owned, start
59 // timer and if all members' services are owned, stop timer.
60 if (std::any_of(_groups.begin(), _groups.end(), [](const auto& group) {
61 const auto& members = group.getMembers();
62 return std::any_of(
63 members.begin(), members.end(),
64 [&group](const auto& member) {
65 return !Manager::hasOwner(member, group.getInterface());
66 });
67 }))
68 {
69 startTimer();
70 }
71 else
72 {
73 stopTimer();
74 }
75 }
76 else
77 {
78 auto* mgr = zone.getManager();
79 // If all group members have a given value and it matches what's
80 // in the cache, start timer and if any do not match, stop
81 // timer.
82 if (std::all_of(
83 _groups.begin(), _groups.end(), [&mgr](const auto& group) {
84 const auto& members = group.getMembers();
85 return std::all_of(
86 members.begin(), members.end(),
87 [&mgr, &group](const auto& member) {
88 return group.getValue() ==
89 mgr->getProperty(member,
90 group.getInterface(),
91 group.getProperty());
92 });
93 }))
94 {
95 // Timer will be started(and never stopped) when _groups is empty
96 startTimer();
97 }
98 else
99 {
100 stopTimer();
101 }
102 }
103 }
104
startTimer()105 void TimerBasedActions::startTimer()
106 {
107 if (!_timer.isEnabled())
108 {
109 if (_type == TimerType::repeating)
110 {
111 _timer.restart(_interval);
112 }
113 else if (_type == TimerType::oneshot)
114 {
115 _timer.restartOnce(_interval);
116 }
117 }
118 }
119
stopTimer()120 void TimerBasedActions::stopTimer()
121 {
122 if (_timer.isEnabled())
123 {
124 _timer.setEnabled(false);
125 }
126 else
127 {
128 // Perform the actions in case state changed after the configured time
129 std::for_each(_actions.begin(), _actions.end(),
130 [](auto& action) { action->run(); });
131 }
132 }
133
timerExpired()134 void TimerBasedActions::timerExpired()
135 {
136 // Perform the actions
137 std::for_each(_actions.begin(), _actions.end(),
138 [](auto& action) { action->run(); });
139 }
140
setZones(std::vector<std::reference_wrapper<Zone>> & zones)141 void TimerBasedActions::setZones(
142 std::vector<std::reference_wrapper<Zone>>& zones)
143 {
144 for (auto& zone : zones)
145 {
146 this->addZone(zone);
147 // Add zone to _actions
148 std::for_each(_actions.begin(), _actions.end(),
149 [&zone](std::unique_ptr<ActionBase>& action) {
150 action->addZone(zone);
151 });
152 }
153 }
154
setTimerConf(const json & jsonObj)155 void TimerBasedActions::setTimerConf(const json& jsonObj)
156 {
157 if (!jsonObj.contains("timer"))
158 {
159 throw ActionParseError{getName(), "Missing required timer entry"};
160 }
161 auto jsonTimer = jsonObj["timer"];
162 if (!jsonTimer.contains("interval") || !jsonTimer.contains("type"))
163 {
164 throw ActionParseError{
165 getName(), "Missing required timer parameters {interval, type}"};
166 }
167
168 // Interval provided in microseconds
169 _interval = static_cast<std::chrono::microseconds>(
170 jsonTimer["interval"].get<uint64_t>());
171
172 // Retrieve type of timer
173 auto type = jsonTimer["type"].get<std::string>();
174 if (type == "oneshot")
175 {
176 _type = TimerType::oneshot;
177 }
178 else if (type == "repeating")
179 {
180 _type = TimerType::repeating;
181 }
182 else
183 {
184 throw ActionParseError{
185 getName(), std::format("Timer type '{}' is not supported", type)};
186 }
187 }
188
setActions(const json & jsonObj)189 void TimerBasedActions::setActions(const json& jsonObj)
190 {
191 if (!jsonObj.contains("actions"))
192 {
193 throw ActionParseError{getName(), "Missing required actions entry"};
194 }
195 for (const auto& jsonAct : jsonObj["actions"])
196 {
197 if (!jsonAct.contains("name"))
198 {
199 throw ActionParseError{getName(), "Missing required action name"};
200 }
201
202 // Get any configured profile restrictions on the action
203 std::vector<std::string> profiles;
204 if (jsonAct.contains("profiles"))
205 {
206 for (const auto& profile : jsonAct["profiles"])
207 {
208 profiles.emplace_back(profile.get<std::string>());
209 }
210 }
211
212 // Set the groups configured for each action run when the timer expires
213 std::vector<Group> groups;
214 Event::setGroups(jsonAct, profiles, groups);
215
216 // List of zones is set on these actions by overriden setZones()
217 auto actObj = ActionFactory::getAction(
218 jsonAct["name"].get<std::string>(), jsonAct, std::move(groups), {});
219 if (actObj)
220 {
221 _actions.emplace_back(std::move(actObj));
222 }
223 }
224 }
225
226 } // namespace phosphor::fan::control::json
227