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