xref: /openbmc/phosphor-fan-presence/control/json/actions/timer_based_actions.cpp (revision dfddd648cb81b27492afead4e2346f5fcd1397cb)
1cb112a3fSMatthew Barth /**
2cb112a3fSMatthew Barth  * Copyright © 2021 IBM Corporation
3cb112a3fSMatthew Barth  *
4cb112a3fSMatthew Barth  * Licensed under the Apache License, Version 2.0 (the "License");
5cb112a3fSMatthew Barth  * you may not use this file except in compliance with the License.
6cb112a3fSMatthew Barth  * You may obtain a copy of the License at
7cb112a3fSMatthew Barth  *
8cb112a3fSMatthew Barth  *     http://www.apache.org/licenses/LICENSE-2.0
9cb112a3fSMatthew Barth  *
10cb112a3fSMatthew Barth  * Unless required by applicable law or agreed to in writing, software
11cb112a3fSMatthew Barth  * distributed under the License is distributed on an "AS IS" BASIS,
12cb112a3fSMatthew Barth  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cb112a3fSMatthew Barth  * See the License for the specific language governing permissions and
14cb112a3fSMatthew Barth  * limitations under the License.
15cb112a3fSMatthew Barth  */
16cb112a3fSMatthew Barth #include "timer_based_actions.hpp"
17cb112a3fSMatthew Barth 
18cb112a3fSMatthew Barth #include "../manager.hpp"
19cb112a3fSMatthew Barth #include "action.hpp"
20cb112a3fSMatthew Barth #include "event.hpp"
21cb112a3fSMatthew Barth #include "group.hpp"
22cb112a3fSMatthew Barth #include "sdbusplus.hpp"
23cb112a3fSMatthew Barth #include "sdeventplus.hpp"
24cb112a3fSMatthew Barth #include "zone.hpp"
25cb112a3fSMatthew Barth 
26cb112a3fSMatthew Barth #include <nlohmann/json.hpp>
27cb112a3fSMatthew Barth 
28cb112a3fSMatthew Barth #include <algorithm>
29cb112a3fSMatthew Barth #include <chrono>
30fbf4703fSPatrick Williams #include <format>
31cb112a3fSMatthew Barth 
32cb112a3fSMatthew Barth namespace phosphor::fan::control::json
33cb112a3fSMatthew Barth {
34cb112a3fSMatthew Barth 
35cb112a3fSMatthew Barth using json = nlohmann::json;
36cb112a3fSMatthew Barth 
TimerBasedActions(const json & jsonObj,const std::vector<Group> & groups)37cb112a3fSMatthew Barth TimerBasedActions::TimerBasedActions(const json& jsonObj,
38cb112a3fSMatthew Barth                                      const std::vector<Group>& groups) :
39cb112a3fSMatthew Barth     ActionBase(jsonObj, groups),
40cb112a3fSMatthew Barth     _timer(util::SDEventPlus::getEvent(),
41cb112a3fSMatthew Barth            std::bind(&TimerBasedActions::timerExpired, this))
42cb112a3fSMatthew Barth {
43cb112a3fSMatthew Barth     // If any of groups' value == nullopt(i.e. not configured), action is
44cb112a3fSMatthew Barth     // driven by the service owned state of the group members
45*dfddd648SPatrick Williams     _byOwner =
46*dfddd648SPatrick Williams         std::any_of(_groups.begin(), _groups.end(), [](const auto& group) {
47*dfddd648SPatrick Williams             return group.getValue() == std::nullopt;
48*dfddd648SPatrick Williams         });
49cb112a3fSMatthew Barth 
50cb112a3fSMatthew Barth     setTimerConf(jsonObj);
51cb112a3fSMatthew Barth     setActions(jsonObj);
52cb112a3fSMatthew Barth }
53cb112a3fSMatthew Barth 
run(Zone & zone)54cb112a3fSMatthew Barth void TimerBasedActions::run(Zone& zone)
55cb112a3fSMatthew Barth {
56cb112a3fSMatthew Barth     if (_byOwner)
57cb112a3fSMatthew Barth     {
58cb112a3fSMatthew Barth         // If any service providing a group member is not owned, start
59cb112a3fSMatthew Barth         // timer and if all members' services are owned, stop timer.
60cb112a3fSMatthew Barth         if (std::any_of(_groups.begin(), _groups.end(), [](const auto& group) {
61cb112a3fSMatthew Barth                 const auto& members = group.getMembers();
62*dfddd648SPatrick Williams                 return std::any_of(
63*dfddd648SPatrick Williams                     members.begin(), members.end(),
64cb112a3fSMatthew Barth                     [&group](const auto& member) {
6561b73296SPatrick Williams                         return !Manager::hasOwner(member, group.getInterface());
66cb112a3fSMatthew Barth                     });
67cb112a3fSMatthew Barth             }))
68cb112a3fSMatthew Barth         {
69cb112a3fSMatthew Barth             startTimer();
70cb112a3fSMatthew Barth         }
71cb112a3fSMatthew Barth         else
72cb112a3fSMatthew Barth         {
73cb112a3fSMatthew Barth             stopTimer();
74cb112a3fSMatthew Barth         }
75cb112a3fSMatthew Barth     }
76cb112a3fSMatthew Barth     else
77cb112a3fSMatthew Barth     {
78cb112a3fSMatthew Barth         auto* mgr = zone.getManager();
79cb112a3fSMatthew Barth         // If all group members have a given value and it matches what's
80cb112a3fSMatthew Barth         // in the cache, start timer and if any do not match, stop
81cb112a3fSMatthew Barth         // timer.
82*dfddd648SPatrick Williams         if (std::all_of(
83*dfddd648SPatrick Williams                 _groups.begin(), _groups.end(), [&mgr](const auto& group) {
84cb112a3fSMatthew Barth                     const auto& members = group.getMembers();
85*dfddd648SPatrick Williams                     return std::all_of(
86*dfddd648SPatrick Williams                         members.begin(), members.end(),
87cb112a3fSMatthew Barth                         [&mgr, &group](const auto& member) {
88cb112a3fSMatthew Barth                             return group.getValue() ==
89*dfddd648SPatrick Williams                                    mgr->getProperty(member,
90*dfddd648SPatrick Williams                                                     group.getInterface(),
91cb112a3fSMatthew Barth                                                     group.getProperty());
92cb112a3fSMatthew Barth                         });
93cb112a3fSMatthew Barth                 }))
94cb112a3fSMatthew Barth         {
95cb112a3fSMatthew Barth             // Timer will be started(and never stopped) when _groups is empty
96cb112a3fSMatthew Barth             startTimer();
97cb112a3fSMatthew Barth         }
98cb112a3fSMatthew Barth         else
99cb112a3fSMatthew Barth         {
100cb112a3fSMatthew Barth             stopTimer();
101cb112a3fSMatthew Barth         }
102cb112a3fSMatthew Barth     }
103cb112a3fSMatthew Barth }
104cb112a3fSMatthew Barth 
startTimer()105cb112a3fSMatthew Barth void TimerBasedActions::startTimer()
106cb112a3fSMatthew Barth {
107cb112a3fSMatthew Barth     if (!_timer.isEnabled())
108cb112a3fSMatthew Barth     {
109cb112a3fSMatthew Barth         if (_type == TimerType::repeating)
110cb112a3fSMatthew Barth         {
111cb112a3fSMatthew Barth             _timer.restart(_interval);
112cb112a3fSMatthew Barth         }
113cb112a3fSMatthew Barth         else if (_type == TimerType::oneshot)
114cb112a3fSMatthew Barth         {
115cb112a3fSMatthew Barth             _timer.restartOnce(_interval);
116cb112a3fSMatthew Barth         }
117cb112a3fSMatthew Barth     }
118cb112a3fSMatthew Barth }
119cb112a3fSMatthew Barth 
stopTimer()120cb112a3fSMatthew Barth void TimerBasedActions::stopTimer()
121cb112a3fSMatthew Barth {
122cb112a3fSMatthew Barth     if (_timer.isEnabled())
123cb112a3fSMatthew Barth     {
124cb112a3fSMatthew Barth         _timer.setEnabled(false);
125cb112a3fSMatthew Barth     }
1267f23e2c3SMatthew Barth     else
1277f23e2c3SMatthew Barth     {
1287f23e2c3SMatthew Barth         // Perform the actions in case state changed after the configured time
1297f23e2c3SMatthew Barth         std::for_each(_actions.begin(), _actions.end(),
1307f23e2c3SMatthew Barth                       [](auto& action) { action->run(); });
1317f23e2c3SMatthew Barth     }
132cb112a3fSMatthew Barth }
133cb112a3fSMatthew Barth 
timerExpired()134cb112a3fSMatthew Barth void TimerBasedActions::timerExpired()
135cb112a3fSMatthew Barth {
136cb112a3fSMatthew Barth     // Perform the actions
137cb112a3fSMatthew Barth     std::for_each(_actions.begin(), _actions.end(),
138cb112a3fSMatthew Barth                   [](auto& action) { action->run(); });
139cb112a3fSMatthew Barth }
140cb112a3fSMatthew Barth 
setZones(std::vector<std::reference_wrapper<Zone>> & zones)141cb112a3fSMatthew Barth void TimerBasedActions::setZones(
142cb112a3fSMatthew Barth     std::vector<std::reference_wrapper<Zone>>& zones)
143cb112a3fSMatthew Barth {
144cb112a3fSMatthew Barth     for (auto& zone : zones)
145cb112a3fSMatthew Barth     {
146cb112a3fSMatthew Barth         this->addZone(zone);
147cb112a3fSMatthew Barth         // Add zone to _actions
148cb112a3fSMatthew Barth         std::for_each(_actions.begin(), _actions.end(),
149cb112a3fSMatthew Barth                       [&zone](std::unique_ptr<ActionBase>& action) {
150cb112a3fSMatthew Barth                           action->addZone(zone);
151cb112a3fSMatthew Barth                       });
152cb112a3fSMatthew Barth     }
153cb112a3fSMatthew Barth }
154cb112a3fSMatthew Barth 
setTimerConf(const json & jsonObj)155cb112a3fSMatthew Barth void TimerBasedActions::setTimerConf(const json& jsonObj)
156cb112a3fSMatthew Barth {
157cb112a3fSMatthew Barth     if (!jsonObj.contains("timer"))
158cb112a3fSMatthew Barth     {
159cb112a3fSMatthew Barth         throw ActionParseError{getName(), "Missing required timer entry"};
160cb112a3fSMatthew Barth     }
161cb112a3fSMatthew Barth     auto jsonTimer = jsonObj["timer"];
162cb112a3fSMatthew Barth     if (!jsonTimer.contains("interval") || !jsonTimer.contains("type"))
163cb112a3fSMatthew Barth     {
164cb112a3fSMatthew Barth         throw ActionParseError{
165cb112a3fSMatthew Barth             getName(), "Missing required timer parameters {interval, type}"};
166cb112a3fSMatthew Barth     }
167cb112a3fSMatthew Barth 
168cb112a3fSMatthew Barth     // Interval provided in microseconds
169cb112a3fSMatthew Barth     _interval = static_cast<std::chrono::microseconds>(
170cb112a3fSMatthew Barth         jsonTimer["interval"].get<uint64_t>());
171cb112a3fSMatthew Barth 
172cb112a3fSMatthew Barth     // Retrieve type of timer
173cb112a3fSMatthew Barth     auto type = jsonTimer["type"].get<std::string>();
174cb112a3fSMatthew Barth     if (type == "oneshot")
175cb112a3fSMatthew Barth     {
176cb112a3fSMatthew Barth         _type = TimerType::oneshot;
177cb112a3fSMatthew Barth     }
178cb112a3fSMatthew Barth     else if (type == "repeating")
179cb112a3fSMatthew Barth     {
180cb112a3fSMatthew Barth         _type = TimerType::repeating;
181cb112a3fSMatthew Barth     }
182cb112a3fSMatthew Barth     else
183cb112a3fSMatthew Barth     {
184cb112a3fSMatthew Barth         throw ActionParseError{
185fbf4703fSPatrick Williams             getName(), std::format("Timer type '{}' is not supported", type)};
186cb112a3fSMatthew Barth     }
187cb112a3fSMatthew Barth }
188cb112a3fSMatthew Barth 
setActions(const json & jsonObj)189cb112a3fSMatthew Barth void TimerBasedActions::setActions(const json& jsonObj)
190cb112a3fSMatthew Barth {
191cb112a3fSMatthew Barth     if (!jsonObj.contains("actions"))
192cb112a3fSMatthew Barth     {
193cb112a3fSMatthew Barth         throw ActionParseError{getName(), "Missing required actions entry"};
194cb112a3fSMatthew Barth     }
195cb112a3fSMatthew Barth     for (const auto& jsonAct : jsonObj["actions"])
196cb112a3fSMatthew Barth     {
197cb112a3fSMatthew Barth         if (!jsonAct.contains("name"))
198cb112a3fSMatthew Barth         {
199cb112a3fSMatthew Barth             throw ActionParseError{getName(), "Missing required action name"};
200cb112a3fSMatthew Barth         }
201cb112a3fSMatthew Barth 
202cb112a3fSMatthew Barth         // Get any configured profile restrictions on the action
203cb112a3fSMatthew Barth         std::vector<std::string> profiles;
204cb112a3fSMatthew Barth         if (jsonAct.contains("profiles"))
205cb112a3fSMatthew Barth         {
206cb112a3fSMatthew Barth             for (const auto& profile : jsonAct["profiles"])
207cb112a3fSMatthew Barth             {
208cb112a3fSMatthew Barth                 profiles.emplace_back(profile.get<std::string>());
209cb112a3fSMatthew Barth             }
210cb112a3fSMatthew Barth         }
211cb112a3fSMatthew Barth 
212cb112a3fSMatthew Barth         // Set the groups configured for each action run when the timer expires
213cb112a3fSMatthew Barth         std::vector<Group> groups;
214cb112a3fSMatthew Barth         Event::setGroups(jsonAct, profiles, groups);
215cb112a3fSMatthew Barth 
216cb112a3fSMatthew Barth         // List of zones is set on these actions by overriden setZones()
217cb112a3fSMatthew Barth         auto actObj = ActionFactory::getAction(
218cb112a3fSMatthew Barth             jsonAct["name"].get<std::string>(), jsonAct, std::move(groups), {});
219cb112a3fSMatthew Barth         if (actObj)
220cb112a3fSMatthew Barth         {
221cb112a3fSMatthew Barth             _actions.emplace_back(std::move(actObj));
222cb112a3fSMatthew Barth         }
223cb112a3fSMatthew Barth     }
224cb112a3fSMatthew Barth }
225cb112a3fSMatthew Barth 
226cb112a3fSMatthew Barth } // namespace phosphor::fan::control::json
227