/** * Copyright © 2021 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "timer_based_actions.hpp" #include "../manager.hpp" #include "action.hpp" #include "event.hpp" #include "group.hpp" #include "sdbusplus.hpp" #include "sdeventplus.hpp" #include "zone.hpp" #include #include #include #include namespace phosphor::fan::control::json { using json = nlohmann::json; TimerBasedActions::TimerBasedActions(const json& jsonObj, const std::vector& groups) : ActionBase(jsonObj, groups), _timer(util::SDEventPlus::getEvent(), std::bind(&TimerBasedActions::timerExpired, this)) { // If any of groups' value == nullopt(i.e. not configured), action is // driven by the service owned state of the group members _byOwner = std::any_of(_groups.begin(), _groups.end(), [](const auto& group) { return group.getValue() == std::nullopt; }); setTimerConf(jsonObj); setActions(jsonObj); } void TimerBasedActions::run(Zone& zone) { if (_byOwner) { // If any service providing a group member is not owned, start // timer and if all members' services are owned, stop timer. if (std::any_of(_groups.begin(), _groups.end(), [](const auto& group) { const auto& members = group.getMembers(); return std::any_of( members.begin(), members.end(), [&group](const auto& member) { return !Manager::hasOwner(member, group.getInterface()); }); })) { startTimer(); } else { stopTimer(); } } else { auto* mgr = zone.getManager(); // If all group members have a given value and it matches what's // in the cache, start timer and if any do not match, stop // timer. if (std::all_of( _groups.begin(), _groups.end(), [&mgr](const auto& group) { const auto& members = group.getMembers(); return std::all_of( members.begin(), members.end(), [&mgr, &group](const auto& member) { return group.getValue() == mgr->getProperty(member, group.getInterface(), group.getProperty()); }); })) { // Timer will be started(and never stopped) when _groups is empty startTimer(); } else { stopTimer(); } } } void TimerBasedActions::startTimer() { if (!_timer.isEnabled()) { if (_type == TimerType::repeating) { _timer.restart(_interval); } else if (_type == TimerType::oneshot) { _timer.restartOnce(_interval); } } } void TimerBasedActions::stopTimer() { if (_timer.isEnabled()) { _timer.setEnabled(false); } else { // Perform the actions in case state changed after the configured time std::for_each(_actions.begin(), _actions.end(), [](auto& action) { action->run(); }); } } void TimerBasedActions::timerExpired() { // Perform the actions std::for_each(_actions.begin(), _actions.end(), [](auto& action) { action->run(); }); } void TimerBasedActions::setZones( std::vector>& zones) { for (auto& zone : zones) { this->addZone(zone); // Add zone to _actions std::for_each(_actions.begin(), _actions.end(), [&zone](std::unique_ptr& action) { action->addZone(zone); }); } } void TimerBasedActions::setTimerConf(const json& jsonObj) { if (!jsonObj.contains("timer")) { throw ActionParseError{getName(), "Missing required timer entry"}; } auto jsonTimer = jsonObj["timer"]; if (!jsonTimer.contains("interval") || !jsonTimer.contains("type")) { throw ActionParseError{ getName(), "Missing required timer parameters {interval, type}"}; } // Interval provided in microseconds _interval = static_cast( jsonTimer["interval"].get()); // Retrieve type of timer auto type = jsonTimer["type"].get(); if (type == "oneshot") { _type = TimerType::oneshot; } else if (type == "repeating") { _type = TimerType::repeating; } else { throw ActionParseError{ getName(), std::format("Timer type '{}' is not supported", type)}; } } void TimerBasedActions::setActions(const json& jsonObj) { if (!jsonObj.contains("actions")) { throw ActionParseError{getName(), "Missing required actions entry"}; } for (const auto& jsonAct : jsonObj["actions"]) { if (!jsonAct.contains("name")) { throw ActionParseError{getName(), "Missing required action name"}; } // Get any configured profile restrictions on the action std::vector profiles; if (jsonAct.contains("profiles")) { for (const auto& profile : jsonAct["profiles"]) { profiles.emplace_back(profile.get()); } } // Set the groups configured for each action run when the timer expires std::vector groups; Event::setGroups(jsonAct, profiles, groups); // List of zones is set on these actions by overriden setZones() auto actObj = ActionFactory::getAction( jsonAct["name"].get(), jsonAct, std::move(groups), {}); if (actObj) { _actions.emplace_back(std::move(actObj)); } } } } // namespace phosphor::fan::control::json