xref: /openbmc/phosphor-fan-presence/control/json/manager.cpp (revision 9f1632e5789a667298c6d2a6489f917bb612a182)
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,
5012cb125aSMatthew Barth          std::map<std::pair<std::string, 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
99*9f1632e5SMatthew 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     }
15112cb125aSMatthew Barth     for (const auto& serv : itServ->second)
15212cb125aSMatthew Barth     {
15312cb125aSMatthew Barth         auto itIntf = std::find_if(
15412cb125aSMatthew Barth             serv.second.begin(), serv.second.end(),
15512cb125aSMatthew Barth             [&intf](const auto& interface) { return intf == interface; });
15612cb125aSMatthew Barth         if (itIntf != std::end(serv.second))
15712cb125aSMatthew Barth         {
15812cb125aSMatthew Barth             // Service found, return owner state
15912cb125aSMatthew Barth             return serv.first.second;
16012cb125aSMatthew Barth         }
16112cb125aSMatthew Barth     }
16212cb125aSMatthew Barth     // Interface not found in cache, therefore owner missing
16312cb125aSMatthew Barth     return false;
16412cb125aSMatthew Barth }
16512cb125aSMatthew Barth 
166d9cb63b6SMatthew Barth void Manager::addTimer(const TimerType type,
167d9cb63b6SMatthew Barth                        const std::chrono::microseconds interval,
168d9cb63b6SMatthew Barth                        std::unique_ptr<TimerPkg> pkg)
169d9cb63b6SMatthew Barth {
170d9cb63b6SMatthew Barth     auto dataPtr =
171d9cb63b6SMatthew Barth         std::make_unique<TimerData>(std::make_pair(type, std::move(*pkg)));
172d9cb63b6SMatthew Barth     Timer timer(_event,
173d9cb63b6SMatthew Barth                 std::bind(&Manager::timerExpired, this, std::ref(*dataPtr)));
174d9cb63b6SMatthew Barth     if (type == TimerType::repeating)
175d9cb63b6SMatthew Barth     {
176d9cb63b6SMatthew Barth         timer.restart(interval);
177d9cb63b6SMatthew Barth     }
178d9cb63b6SMatthew Barth     else if (type == TimerType::oneshot)
179d9cb63b6SMatthew Barth     {
180d9cb63b6SMatthew Barth         timer.restartOnce(interval);
181d9cb63b6SMatthew Barth     }
182d9cb63b6SMatthew Barth     else
183d9cb63b6SMatthew Barth     {
184d9cb63b6SMatthew Barth         throw std::invalid_argument("Invalid Timer Type");
185d9cb63b6SMatthew Barth     }
186d9cb63b6SMatthew Barth     _timers.emplace_back(std::move(dataPtr), std::move(timer));
187d9cb63b6SMatthew Barth }
188d9cb63b6SMatthew Barth 
189d9cb63b6SMatthew Barth void Manager::timerExpired(TimerData& data)
190d9cb63b6SMatthew Barth {
191d9cb63b6SMatthew Barth     auto& actions =
192d9cb63b6SMatthew Barth         std::get<std::vector<std::unique_ptr<ActionBase>>&>(data.second);
193d9cb63b6SMatthew Barth     // Perform the actions in the timer data
194d9cb63b6SMatthew Barth     std::for_each(actions.begin(), actions.end(),
195d9cb63b6SMatthew Barth                   [zone = std::ref(std::get<Zone&>(data.second)),
196d9cb63b6SMatthew Barth                    group = std::cref(std::get<const Group&>(data.second))](
197d9cb63b6SMatthew Barth                       auto& action) { action->run(zone, group); });
198d9cb63b6SMatthew Barth 
199d9cb63b6SMatthew Barth     // Remove oneshot timers after they expired
200d9cb63b6SMatthew Barth     if (data.first == TimerType::oneshot)
201d9cb63b6SMatthew Barth     {
202d9cb63b6SMatthew Barth         auto itTimer = std::find_if(
203d9cb63b6SMatthew Barth             _timers.begin(), _timers.end(), [&data](const auto& timer) {
204d9cb63b6SMatthew Barth                 return (data.first == timer.first->first &&
205d9cb63b6SMatthew Barth                         (std::get<std::string>(data.second) ==
206d9cb63b6SMatthew Barth                          std::get<std::string>(timer.first->second)));
207d9cb63b6SMatthew Barth             });
208d9cb63b6SMatthew Barth         if (itTimer != std::end(_timers))
209d9cb63b6SMatthew Barth         {
210d9cb63b6SMatthew Barth             _timers.erase(itTimer);
211d9cb63b6SMatthew Barth         }
212d9cb63b6SMatthew Barth     }
213d9cb63b6SMatthew Barth }
214d9cb63b6SMatthew Barth 
215a227a16dSMatthew Barth unsigned int Manager::getPowerOnDelay()
216a227a16dSMatthew Barth {
217a227a16dSMatthew Barth     auto powerOnDelay = 0;
218a227a16dSMatthew Barth 
219a227a16dSMatthew Barth     // Parse optional "power_on_delay" from JSON object
220a227a16dSMatthew Barth     if (!_jsonObj.empty() && _jsonObj.contains("power_on_delay"))
221a227a16dSMatthew Barth     {
222a227a16dSMatthew Barth         powerOnDelay = _jsonObj["power_on_delay"].get<unsigned int>();
223a227a16dSMatthew Barth     }
224a227a16dSMatthew Barth 
225a227a16dSMatthew Barth     return powerOnDelay;
226a227a16dSMatthew Barth }
227a227a16dSMatthew Barth 
228acd737cdSMatthew Barth void Manager::setProfiles()
229acd737cdSMatthew Barth {
230acd737cdSMatthew Barth     // Profiles JSON config file is optional
231acd737cdSMatthew Barth     auto confFile = fan::JsonConfig::getConfFile(_bus, confAppName,
232acd737cdSMatthew Barth                                                  Profile::confFileName, true);
233acd737cdSMatthew Barth     if (!confFile.empty())
234acd737cdSMatthew Barth     {
235acd737cdSMatthew Barth         for (const auto& entry : fan::JsonConfig::load(confFile))
236acd737cdSMatthew Barth         {
237acd737cdSMatthew Barth             auto obj = std::make_unique<Profile>(entry);
238acd737cdSMatthew Barth             _profiles.emplace(
239acd737cdSMatthew Barth                 std::make_pair(obj->getName(), obj->getProfiles()),
240acd737cdSMatthew Barth                 std::move(obj));
241acd737cdSMatthew Barth         }
242acd737cdSMatthew Barth     }
243acd737cdSMatthew Barth     // Ensure all configurations use the same set of active profiles
244acd737cdSMatthew Barth     // (In case a profile's active state changes during configuration)
245acd737cdSMatthew Barth     for (const auto& profile : _profiles)
246acd737cdSMatthew Barth     {
247acd737cdSMatthew Barth         if (profile.second->isActive())
248acd737cdSMatthew Barth         {
249acd737cdSMatthew Barth             _activeProfiles.emplace_back(profile.first.first);
250acd737cdSMatthew Barth         }
251acd737cdSMatthew Barth     }
252acd737cdSMatthew Barth }
253acd737cdSMatthew Barth 
254a227a16dSMatthew Barth } // namespace phosphor::fan::control::json
255