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