xref: /openbmc/phosphor-fan-presence/control/json/manager.cpp (revision 4ca87fae7b45fe8d363a999a0d0c1bbf5a8da78d)
1a227a16dSMatthew Barth /**
2a227a16dSMatthew Barth  * Copyright © 2020 IBM Corporation
3a227a16dSMatthew Barth  *
4a227a16dSMatthew Barth  * Licensed under the Apache License, Version 2.0 (the "License");
5a227a16dSMatthew Barth  * you may not use this file except in compliance with the License.
6a227a16dSMatthew Barth  * You may obtain a copy of the License at
7a227a16dSMatthew Barth  *
8a227a16dSMatthew Barth  *     http://www.apache.org/licenses/LICENSE-2.0
9a227a16dSMatthew Barth  *
10a227a16dSMatthew Barth  * Unless required by applicable law or agreed to in writing, software
11a227a16dSMatthew Barth  * distributed under the License is distributed on an "AS IS" BASIS,
12a227a16dSMatthew Barth  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a227a16dSMatthew Barth  * See the License for the specific language governing permissions and
14a227a16dSMatthew Barth  * limitations under the License.
15a227a16dSMatthew Barth  */
16b584d818SMatthew Barth #include "config.h"
17b584d818SMatthew Barth 
18a227a16dSMatthew Barth #include "manager.hpp"
19a227a16dSMatthew Barth 
20d9cb63b6SMatthew Barth #include "action.hpp"
2144ab7693SMatthew Barth #include "event.hpp"
22de90fb4dSMatthew Barth #include "fan.hpp"
23d9cb63b6SMatthew Barth #include "group.hpp"
24a227a16dSMatthew Barth #include "json_config.hpp"
2506764946SMatthew Barth #include "profile.hpp"
26acd737cdSMatthew Barth #include "zone.hpp"
27a227a16dSMatthew Barth 
28acd737cdSMatthew Barth #include <nlohmann/json.hpp>
29a227a16dSMatthew Barth #include <sdbusplus/bus.hpp>
30acd737cdSMatthew Barth #include <sdeventplus/event.hpp>
31d9cb63b6SMatthew Barth #include <sdeventplus/utility/timer.hpp>
32a227a16dSMatthew Barth 
33de90fb4dSMatthew Barth #include <algorithm>
34d9cb63b6SMatthew Barth #include <chrono>
35a227a16dSMatthew Barth #include <filesystem>
36d9cb63b6SMatthew Barth #include <functional>
37d9cb63b6SMatthew Barth #include <map>
38d9cb63b6SMatthew Barth #include <memory>
39d9cb63b6SMatthew Barth #include <tuple>
40d9cb63b6SMatthew Barth #include <utility>
4106764946SMatthew Barth #include <vector>
42a227a16dSMatthew Barth 
43a227a16dSMatthew Barth namespace phosphor::fan::control::json
44a227a16dSMatthew Barth {
45a227a16dSMatthew Barth 
46acd737cdSMatthew Barth using json = nlohmann::json;
47acd737cdSMatthew Barth 
48acd737cdSMatthew Barth std::vector<std::string> Manager::_activeProfiles;
4912cb125aSMatthew Barth std::map<std::string,
50*4ca87faeSMatthew Barth          std::map<std::string, std::pair<bool, std::vector<std::string>>>>
5112cb125aSMatthew Barth     Manager::_servTree;
5207fecfc6SMatthew Barth std::map<std::string,
5307fecfc6SMatthew Barth          std::map<std::string, std::map<std::string, PropertyVariantType>>>
5407fecfc6SMatthew Barth     Manager::_objects;
55acd737cdSMatthew Barth 
5606764946SMatthew Barth Manager::Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event) :
57acd737cdSMatthew Barth     _bus(bus), _event(event)
58a227a16dSMatthew Barth {
59a227a16dSMatthew Barth     // Manager JSON config file is optional
60a227a16dSMatthew Barth     auto confFile =
61a227a16dSMatthew Barth         fan::JsonConfig::getConfFile(bus, confAppName, confFileName, true);
62a227a16dSMatthew Barth     if (!confFile.empty())
63a227a16dSMatthew Barth     {
64a227a16dSMatthew Barth         _jsonObj = fan::JsonConfig::load(confFile);
65a227a16dSMatthew Barth     }
6606764946SMatthew Barth 
67acd737cdSMatthew Barth     // Parse and set the available profiles and which are active
68acd737cdSMatthew Barth     setProfiles();
69acd737cdSMatthew Barth 
70acd737cdSMatthew Barth     // Load the zone configurations
71603ef164SMatthew Barth     _zones = getConfig<Zone>(false, bus, bus, event, this);
72de90fb4dSMatthew Barth 
73de90fb4dSMatthew Barth     // Load the fan configurations and move each fan into its zone
74603ef164SMatthew Barth     auto fans = getConfig<Fan>(false, bus, bus);
75de90fb4dSMatthew Barth     for (auto& fan : fans)
76de90fb4dSMatthew Barth     {
770206c728SMatthew Barth         configKey fanProfile =
780206c728SMatthew Barth             std::make_pair(fan.second->getZone(), fan.first.second);
790206c728SMatthew Barth         auto itZone = std::find_if(
800206c728SMatthew Barth             _zones.begin(), _zones.end(), [&fanProfile](const auto& zone) {
810206c728SMatthew Barth                 return Manager::inConfig(fanProfile, zone.first);
82de90fb4dSMatthew Barth             });
83de90fb4dSMatthew Barth         if (itZone != _zones.end())
84de90fb4dSMatthew Barth         {
856f787309SMatthew Barth             if (itZone->second->getTarget() != fan.second->getTarget() &&
866f787309SMatthew Barth                 fan.second->getTarget() != 0)
876f787309SMatthew Barth             {
886f787309SMatthew Barth                 // Update zone target to current target of the fan in the zone
896f787309SMatthew Barth                 itZone->second->setTarget(fan.second->getTarget());
906f787309SMatthew Barth             }
91de90fb4dSMatthew Barth             itZone->second->addFan(std::move(fan.second));
92de90fb4dSMatthew Barth         }
93de90fb4dSMatthew Barth     }
94b584d818SMatthew Barth 
9544ab7693SMatthew Barth     // Load the configured groups that are copied into events where they're used
9644ab7693SMatthew Barth     auto groups = getConfig<Group>(true, bus);
9744ab7693SMatthew Barth 
9844ab7693SMatthew Barth     // Load any events configured
999f1632e5SMatthew Barth     _events = getConfig<Event>(true, bus, bus, groups, _zones);
10044ab7693SMatthew Barth 
101b584d818SMatthew Barth     bus.request_name(CONTROL_BUSNAME);
10206764946SMatthew Barth }
103acd737cdSMatthew Barth 
104acd737cdSMatthew Barth const std::vector<std::string>& Manager::getActiveProfiles()
105acd737cdSMatthew Barth {
106acd737cdSMatthew Barth     return _activeProfiles;
107a227a16dSMatthew Barth }
108a227a16dSMatthew Barth 
1090206c728SMatthew Barth bool Manager::inConfig(const configKey& input, const configKey& comp)
1100206c728SMatthew Barth {
1110206c728SMatthew Barth     // Config names dont match, do not include in config
1120206c728SMatthew Barth     if (input.first != comp.first)
1130206c728SMatthew Barth     {
1140206c728SMatthew Barth         return false;
1150206c728SMatthew Barth     }
1160206c728SMatthew Barth     // No profiles specified by input config, can be used in any config
1170206c728SMatthew Barth     if (input.second.empty())
1180206c728SMatthew Barth     {
1190206c728SMatthew Barth         return true;
1200206c728SMatthew Barth     }
1210206c728SMatthew Barth     else
1220206c728SMatthew Barth     {
1230206c728SMatthew Barth         // Profiles must have one match in the other's profiles(and they must be
1240206c728SMatthew Barth         // an active profile) to be used in the config
1250206c728SMatthew Barth         return std::any_of(
1260206c728SMatthew Barth             input.second.begin(), input.second.end(),
1270206c728SMatthew Barth             [&comp](const auto& lProfile) {
1280206c728SMatthew Barth                 return std::any_of(
1290206c728SMatthew Barth                     comp.second.begin(), comp.second.end(),
1300206c728SMatthew Barth                     [&lProfile](const auto& rProfile) {
1310206c728SMatthew Barth                         if (lProfile != rProfile)
1320206c728SMatthew Barth                         {
1330206c728SMatthew Barth                             return false;
1340206c728SMatthew Barth                         }
1350206c728SMatthew Barth                         auto activeProfs = getActiveProfiles();
1360206c728SMatthew Barth                         return std::find(activeProfs.begin(), activeProfs.end(),
1370206c728SMatthew Barth                                          lProfile) != activeProfs.end();
1380206c728SMatthew Barth                     });
1390206c728SMatthew Barth             });
1400206c728SMatthew Barth     }
1410206c728SMatthew Barth }
1420206c728SMatthew Barth 
14312cb125aSMatthew Barth bool Manager::hasOwner(const std::string& path, const std::string& intf)
14412cb125aSMatthew Barth {
14512cb125aSMatthew Barth     auto itServ = _servTree.find(path);
14612cb125aSMatthew Barth     if (itServ == _servTree.end())
14712cb125aSMatthew Barth     {
14812cb125aSMatthew Barth         // Path not found in cache, therefore owner missing
14912cb125aSMatthew Barth         return false;
15012cb125aSMatthew Barth     }
151*4ca87faeSMatthew Barth     for (const auto& service : itServ->second)
15212cb125aSMatthew Barth     {
15312cb125aSMatthew Barth         auto itIntf = std::find_if(
154*4ca87faeSMatthew Barth             service.second.second.begin(), service.second.second.end(),
15512cb125aSMatthew Barth             [&intf](const auto& interface) { return intf == interface; });
156*4ca87faeSMatthew Barth         if (itIntf != std::end(service.second.second))
15712cb125aSMatthew Barth         {
15812cb125aSMatthew Barth             // Service found, return owner state
159*4ca87faeSMatthew Barth             return service.second.first;
16012cb125aSMatthew Barth         }
16112cb125aSMatthew Barth     }
16212cb125aSMatthew Barth     // Interface not found in cache, therefore owner missing
16312cb125aSMatthew Barth     return false;
16412cb125aSMatthew Barth }
16512cb125aSMatthew Barth 
166*4ca87faeSMatthew Barth void Manager::setOwner(const std::string& path, const std::string& serv,
167*4ca87faeSMatthew Barth                        const std::string& intf, bool isOwned)
168*4ca87faeSMatthew Barth {
169*4ca87faeSMatthew Barth     auto itServ = _servTree.find(path);
170*4ca87faeSMatthew Barth     if (itServ == _servTree.end())
171*4ca87faeSMatthew Barth     {
172*4ca87faeSMatthew Barth         auto intfs = {intf};
173*4ca87faeSMatthew Barth         _servTree[path] = {{serv, std::make_pair(isOwned, intfs)}};
174*4ca87faeSMatthew Barth         return;
175*4ca87faeSMatthew Barth     }
176*4ca87faeSMatthew Barth     for (auto& service : itServ->second)
177*4ca87faeSMatthew Barth     {
178*4ca87faeSMatthew Barth         auto itIntf = std::find_if(
179*4ca87faeSMatthew Barth             service.second.second.begin(), service.second.second.end(),
180*4ca87faeSMatthew Barth             [&intf](const auto& interface) { return intf == interface; });
181*4ca87faeSMatthew Barth         if (itIntf != std::end(service.second.second))
182*4ca87faeSMatthew Barth         {
183*4ca87faeSMatthew Barth             if (service.first == serv)
184*4ca87faeSMatthew Barth             {
185*4ca87faeSMatthew Barth                 service.second.first = isOwned;
186*4ca87faeSMatthew Barth                 return;
187*4ca87faeSMatthew Barth             }
188*4ca87faeSMatthew Barth         }
189*4ca87faeSMatthew Barth     }
190*4ca87faeSMatthew Barth     auto intfs = {intf};
191*4ca87faeSMatthew Barth     itServ->second[serv] = std::make_pair(isOwned, intfs);
192*4ca87faeSMatthew Barth }
193*4ca87faeSMatthew Barth 
194*4ca87faeSMatthew Barth const std::string& Manager::findService(const std::string& path,
195*4ca87faeSMatthew Barth                                         const std::string& intf)
196*4ca87faeSMatthew Barth {
197*4ca87faeSMatthew Barth     static const std::string empty = "";
198*4ca87faeSMatthew Barth 
199*4ca87faeSMatthew Barth     auto itServ = _servTree.find(path);
200*4ca87faeSMatthew Barth     if (itServ != _servTree.end())
201*4ca87faeSMatthew Barth     {
202*4ca87faeSMatthew Barth         for (const auto& service : itServ->second)
203*4ca87faeSMatthew Barth         {
204*4ca87faeSMatthew Barth             auto itIntf = std::find_if(
205*4ca87faeSMatthew Barth                 service.second.second.begin(), service.second.second.end(),
206*4ca87faeSMatthew Barth                 [&intf](const auto& interface) { return intf == interface; });
207*4ca87faeSMatthew Barth             if (itIntf != std::end(service.second.second))
208*4ca87faeSMatthew Barth             {
209*4ca87faeSMatthew Barth                 // Service found, return service name
210*4ca87faeSMatthew Barth                 return service.first;
211*4ca87faeSMatthew Barth             }
212*4ca87faeSMatthew Barth         }
213*4ca87faeSMatthew Barth     }
214*4ca87faeSMatthew Barth 
215*4ca87faeSMatthew Barth     return empty;
216*4ca87faeSMatthew Barth }
217*4ca87faeSMatthew Barth 
218*4ca87faeSMatthew Barth void Manager::addServices(const std::string& path, const std::string& intf,
219*4ca87faeSMatthew Barth                           int32_t depth)
220*4ca87faeSMatthew Barth {
221*4ca87faeSMatthew Barth     // Get all subtree objects for the given interface
222*4ca87faeSMatthew Barth     auto objects = util::SDBusPlus::getSubTree(util::SDBusPlus::getBus(), "/",
223*4ca87faeSMatthew Barth                                                intf, depth);
224*4ca87faeSMatthew Barth     // Add what's returned to the cache of path->services
225*4ca87faeSMatthew Barth     for (auto& itPath : objects)
226*4ca87faeSMatthew Barth     {
227*4ca87faeSMatthew Barth         auto pathIter = _servTree.find(itPath.first);
228*4ca87faeSMatthew Barth         if (pathIter != _servTree.end())
229*4ca87faeSMatthew Barth         {
230*4ca87faeSMatthew Barth             // Path found in cache
231*4ca87faeSMatthew Barth             for (auto& itServ : itPath.second)
232*4ca87faeSMatthew Barth             {
233*4ca87faeSMatthew Barth                 auto servIter = pathIter->second.find(itServ.first);
234*4ca87faeSMatthew Barth                 if (servIter != pathIter->second.end())
235*4ca87faeSMatthew Barth                 {
236*4ca87faeSMatthew Barth                     // Service found in cache
237*4ca87faeSMatthew Barth                     for (auto& itIntf : itServ.second)
238*4ca87faeSMatthew Barth                     {
239*4ca87faeSMatthew Barth                         if (std::find(servIter->second.second.begin(),
240*4ca87faeSMatthew Barth                                       servIter->second.second.end(),
241*4ca87faeSMatthew Barth                                       itIntf) == servIter->second.second.end())
242*4ca87faeSMatthew Barth                         {
243*4ca87faeSMatthew Barth                             // Add interface to cache
244*4ca87faeSMatthew Barth                             servIter->second.second.emplace_back(itIntf);
245*4ca87faeSMatthew Barth                         }
246*4ca87faeSMatthew Barth                     }
247*4ca87faeSMatthew Barth                 }
248*4ca87faeSMatthew Barth                 else
249*4ca87faeSMatthew Barth                 {
250*4ca87faeSMatthew Barth                     // Service not found in cache
251*4ca87faeSMatthew Barth                     auto intfs = {intf};
252*4ca87faeSMatthew Barth                     pathIter->second[itServ.first] =
253*4ca87faeSMatthew Barth                         std::make_pair(true, intfs);
254*4ca87faeSMatthew Barth                 }
255*4ca87faeSMatthew Barth             }
256*4ca87faeSMatthew Barth         }
257*4ca87faeSMatthew Barth         else
258*4ca87faeSMatthew Barth         {
259*4ca87faeSMatthew Barth             // Path not found in cache
260*4ca87faeSMatthew Barth             auto intfs = {intf};
261*4ca87faeSMatthew Barth             _servTree[itPath.first] = {
262*4ca87faeSMatthew Barth                 {itPath.second.begin()->first, std::make_pair(true, intfs)}};
263*4ca87faeSMatthew Barth         }
264*4ca87faeSMatthew Barth     }
265*4ca87faeSMatthew Barth }
266*4ca87faeSMatthew Barth 
267*4ca87faeSMatthew Barth const std::string& Manager::getService(const std::string& path,
268*4ca87faeSMatthew Barth                                        const std::string& intf)
269*4ca87faeSMatthew Barth {
270*4ca87faeSMatthew Barth     // Retrieve service from cache
271*4ca87faeSMatthew Barth     const auto& serviceName = findService(path, intf);
272*4ca87faeSMatthew Barth     if (serviceName.empty())
273*4ca87faeSMatthew Barth     {
274*4ca87faeSMatthew Barth         addServices(path, intf, 0);
275*4ca87faeSMatthew Barth         return findService(path, intf);
276*4ca87faeSMatthew Barth     }
277*4ca87faeSMatthew Barth 
278*4ca87faeSMatthew Barth     return serviceName;
279*4ca87faeSMatthew Barth }
280*4ca87faeSMatthew Barth 
281d9cb63b6SMatthew Barth void Manager::addTimer(const TimerType type,
282d9cb63b6SMatthew Barth                        const std::chrono::microseconds interval,
283d9cb63b6SMatthew Barth                        std::unique_ptr<TimerPkg> pkg)
284d9cb63b6SMatthew Barth {
285d9cb63b6SMatthew Barth     auto dataPtr =
286d9cb63b6SMatthew Barth         std::make_unique<TimerData>(std::make_pair(type, std::move(*pkg)));
287d9cb63b6SMatthew Barth     Timer timer(_event,
288d9cb63b6SMatthew Barth                 std::bind(&Manager::timerExpired, this, std::ref(*dataPtr)));
289d9cb63b6SMatthew Barth     if (type == TimerType::repeating)
290d9cb63b6SMatthew Barth     {
291d9cb63b6SMatthew Barth         timer.restart(interval);
292d9cb63b6SMatthew Barth     }
293d9cb63b6SMatthew Barth     else if (type == TimerType::oneshot)
294d9cb63b6SMatthew Barth     {
295d9cb63b6SMatthew Barth         timer.restartOnce(interval);
296d9cb63b6SMatthew Barth     }
297d9cb63b6SMatthew Barth     else
298d9cb63b6SMatthew Barth     {
299d9cb63b6SMatthew Barth         throw std::invalid_argument("Invalid Timer Type");
300d9cb63b6SMatthew Barth     }
301d9cb63b6SMatthew Barth     _timers.emplace_back(std::move(dataPtr), std::move(timer));
302d9cb63b6SMatthew Barth }
303d9cb63b6SMatthew Barth 
304d9cb63b6SMatthew Barth void Manager::timerExpired(TimerData& data)
305d9cb63b6SMatthew Barth {
306d9cb63b6SMatthew Barth     auto& actions =
307d9cb63b6SMatthew Barth         std::get<std::vector<std::unique_ptr<ActionBase>>&>(data.second);
308d9cb63b6SMatthew Barth     // Perform the actions in the timer data
309d9cb63b6SMatthew Barth     std::for_each(actions.begin(), actions.end(),
31000f6aa09SMatthew Barth                   [](auto& action) { action->run(); });
311d9cb63b6SMatthew Barth 
312d9cb63b6SMatthew Barth     // Remove oneshot timers after they expired
313d9cb63b6SMatthew Barth     if (data.first == TimerType::oneshot)
314d9cb63b6SMatthew Barth     {
315d9cb63b6SMatthew Barth         auto itTimer = std::find_if(
316d9cb63b6SMatthew Barth             _timers.begin(), _timers.end(), [&data](const auto& timer) {
317d9cb63b6SMatthew Barth                 return (data.first == timer.first->first &&
318d9cb63b6SMatthew Barth                         (std::get<std::string>(data.second) ==
319d9cb63b6SMatthew Barth                          std::get<std::string>(timer.first->second)));
320d9cb63b6SMatthew Barth             });
321d9cb63b6SMatthew Barth         if (itTimer != std::end(_timers))
322d9cb63b6SMatthew Barth         {
323d9cb63b6SMatthew Barth             _timers.erase(itTimer);
324d9cb63b6SMatthew Barth         }
325d9cb63b6SMatthew Barth     }
326d9cb63b6SMatthew Barth }
327d9cb63b6SMatthew Barth 
328a227a16dSMatthew Barth unsigned int Manager::getPowerOnDelay()
329a227a16dSMatthew Barth {
330a227a16dSMatthew Barth     auto powerOnDelay = 0;
331a227a16dSMatthew Barth 
332a227a16dSMatthew Barth     // Parse optional "power_on_delay" from JSON object
333a227a16dSMatthew Barth     if (!_jsonObj.empty() && _jsonObj.contains("power_on_delay"))
334a227a16dSMatthew Barth     {
335a227a16dSMatthew Barth         powerOnDelay = _jsonObj["power_on_delay"].get<unsigned int>();
336a227a16dSMatthew Barth     }
337a227a16dSMatthew Barth 
338a227a16dSMatthew Barth     return powerOnDelay;
339a227a16dSMatthew Barth }
340a227a16dSMatthew Barth 
341acd737cdSMatthew Barth void Manager::setProfiles()
342acd737cdSMatthew Barth {
343acd737cdSMatthew Barth     // Profiles JSON config file is optional
344acd737cdSMatthew Barth     auto confFile = fan::JsonConfig::getConfFile(_bus, confAppName,
345acd737cdSMatthew Barth                                                  Profile::confFileName, true);
346acd737cdSMatthew Barth     if (!confFile.empty())
347acd737cdSMatthew Barth     {
348acd737cdSMatthew Barth         for (const auto& entry : fan::JsonConfig::load(confFile))
349acd737cdSMatthew Barth         {
350acd737cdSMatthew Barth             auto obj = std::make_unique<Profile>(entry);
351acd737cdSMatthew Barth             _profiles.emplace(
352acd737cdSMatthew Barth                 std::make_pair(obj->getName(), obj->getProfiles()),
353acd737cdSMatthew Barth                 std::move(obj));
354acd737cdSMatthew Barth         }
355acd737cdSMatthew Barth     }
356acd737cdSMatthew Barth     // Ensure all configurations use the same set of active profiles
357acd737cdSMatthew Barth     // (In case a profile's active state changes during configuration)
358acd737cdSMatthew Barth     for (const auto& profile : _profiles)
359acd737cdSMatthew Barth     {
360acd737cdSMatthew Barth         if (profile.second->isActive())
361acd737cdSMatthew Barth         {
362acd737cdSMatthew Barth             _activeProfiles.emplace_back(profile.first.first);
363acd737cdSMatthew Barth         }
364acd737cdSMatthew Barth     }
365acd737cdSMatthew Barth }
366acd737cdSMatthew Barth 
367a227a16dSMatthew Barth } // namespace phosphor::fan::control::json
368