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