14f0d3b74SMatthew Barth /**
2762e858bSMike Capps  * Copyright © 2022 IBM Corporation
34f0d3b74SMatthew Barth  *
44f0d3b74SMatthew Barth  * Licensed under the Apache License, Version 2.0 (the "License");
54f0d3b74SMatthew Barth  * you may not use this file except in compliance with the License.
64f0d3b74SMatthew Barth  * You may obtain a copy of the License at
74f0d3b74SMatthew Barth  *
84f0d3b74SMatthew Barth  *     http://www.apache.org/licenses/LICENSE-2.0
94f0d3b74SMatthew Barth  *
104f0d3b74SMatthew Barth  * Unless required by applicable law or agreed to in writing, software
114f0d3b74SMatthew Barth  * distributed under the License is distributed on an "AS IS" BASIS,
124f0d3b74SMatthew Barth  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134f0d3b74SMatthew Barth  * See the License for the specific language governing permissions and
144f0d3b74SMatthew Barth  * limitations under the License.
154f0d3b74SMatthew Barth  */
164f0d3b74SMatthew Barth #include "zone.hpp"
174f0d3b74SMatthew Barth 
1840554d8cSMatt Spinler #include "../utils/flight_recorder.hpp"
19bc89a8a0SMatthew Barth #include "dbus_zone.hpp"
20de90fb4dSMatthew Barth #include "fan.hpp"
219403a217SMatthew Barth #include "sdbusplus.hpp"
22216229c1SMatthew Barth 
234f0d3b74SMatthew Barth #include <nlohmann/json.hpp>
244f0d3b74SMatthew Barth #include <phosphor-logging/log.hpp>
25603ef164SMatthew Barth #include <sdeventplus/event.hpp>
264f0d3b74SMatthew Barth 
27a0dd1350SMatthew Barth #include <algorithm>
28007de099SMatthew Barth #include <chrono>
29651f03a4SMatthew Barth #include <iterator>
30651f03a4SMatthew Barth #include <map>
31bc89a8a0SMatthew Barth #include <memory>
32651f03a4SMatthew Barth #include <numeric>
33651f03a4SMatthew Barth #include <utility>
34216229c1SMatthew Barth #include <vector>
35651f03a4SMatthew Barth 
364f0d3b74SMatthew Barth namespace phosphor::fan::control::json
374f0d3b74SMatthew Barth {
384f0d3b74SMatthew Barth 
394f0d3b74SMatthew Barth using json = nlohmann::json;
404f0d3b74SMatthew Barth using namespace phosphor::logging;
414f0d3b74SMatthew Barth 
42bc89a8a0SMatthew Barth const std::map<
43bc89a8a0SMatthew Barth     std::string,
44bc89a8a0SMatthew Barth     std::map<std::string, std::function<std::function<void(DBusZone&, Zone&)>(
45b584d818SMatthew Barth                               const json&, bool)>>>
46bc89a8a0SMatthew Barth     Zone::_intfPropHandlers = {
47bc89a8a0SMatthew Barth         {DBusZone::thermalModeIntf,
48bc89a8a0SMatthew Barth          {{DBusZone::supportedProp, zone::property::supported},
49bc89a8a0SMatthew Barth           {DBusZone::currentProp, zone::property::current}}}};
50651f03a4SMatthew Barth 
Zone(const json & jsonObj,const sdeventplus::Event & event,Manager * mgr)519403a217SMatthew Barth Zone::Zone(const json& jsonObj, const sdeventplus::Event& event, Manager* mgr) :
52ab8e4b82SMatthew Barth     ConfigBase(jsonObj), _dbusZone{}, _manager(mgr), _defaultFloor(0),
532504c77cSMatthew Barth     _incDelay(0), _decInterval(0), _floor(0), _target(0), _incDelta(0),
542504c77cSMatthew Barth     _decDelta(0), _requestTargetBase(0), _isActive(true),
55ab8e4b82SMatthew Barth     _incTimer(event, std::bind(&Zone::incTimerExpired, this)),
56007de099SMatthew Barth     _decTimer(event, std::bind(&Zone::decTimerExpired, this))
574f0d3b74SMatthew Barth {
58e47c9588SMatthew Barth     // Increase delay is optional, defaults to 0
594f0d3b74SMatthew Barth     if (jsonObj.contains("increase_delay"))
604f0d3b74SMatthew Barth     {
61007de099SMatthew Barth         _incDelay =
62007de099SMatthew Barth             std::chrono::seconds(jsonObj["increase_delay"].get<uint64_t>());
634f0d3b74SMatthew Barth     }
64ab8e4b82SMatthew Barth 
65ab8e4b82SMatthew Barth     // Poweron target is required
66ab8e4b82SMatthew Barth     setPowerOnTarget(jsonObj);
67ab8e4b82SMatthew Barth 
68ab8e4b82SMatthew Barth     // Default ceiling is optional, defaults to poweron target
69ab8e4b82SMatthew Barth     _defaultCeiling = _poweronTarget;
70ab8e4b82SMatthew Barth     if (jsonObj.contains("default_ceiling"))
71ab8e4b82SMatthew Barth     {
72ab8e4b82SMatthew Barth         _defaultCeiling = jsonObj["default_ceiling"].get<uint64_t>();
73ab8e4b82SMatthew Barth     }
74ab8e4b82SMatthew Barth     // Start with the current ceiling set as the default ceiling
75ab8e4b82SMatthew Barth     _ceiling = _defaultCeiling;
76ab8e4b82SMatthew Barth 
77ab8e4b82SMatthew Barth     // Default floor is optional, defaults to 0
78ab8e4b82SMatthew Barth     if (jsonObj.contains("default_floor"))
79ab8e4b82SMatthew Barth     {
80ab8e4b82SMatthew Barth         _defaultFloor = jsonObj["default_floor"].get<uint64_t>();
81dc3152d3SMatthew Barth         if (_defaultFloor > _ceiling)
82dc3152d3SMatthew Barth         {
83dc3152d3SMatthew Barth             log<level::ERR>(
84fbf4703fSPatrick Williams                 std::format("Configured default_floor({}) above ceiling({}), "
85dc3152d3SMatthew Barth                             "setting default floor to ceiling",
86dc3152d3SMatthew Barth                             _defaultFloor, _ceiling)
87dc3152d3SMatthew Barth                     .c_str());
88dc3152d3SMatthew Barth             _defaultFloor = _ceiling;
89dc3152d3SMatthew Barth         }
90ab8e4b82SMatthew Barth         // Start with the current floor set as the default
91ab8e4b82SMatthew Barth         _floor = _defaultFloor;
92ab8e4b82SMatthew Barth     }
93ab8e4b82SMatthew Barth 
942504c77cSMatthew Barth     // Decrease interval is optional, defaults to 0
952504c77cSMatthew Barth     // A decrease interval of 0sec disables the decrease timer
962504c77cSMatthew Barth     if (jsonObj.contains("decrease_interval"))
972504c77cSMatthew Barth     {
982504c77cSMatthew Barth         _decInterval =
992504c77cSMatthew Barth             std::chrono::seconds(jsonObj["decrease_interval"].get<uint64_t>());
1002504c77cSMatthew Barth     }
101ab8e4b82SMatthew Barth 
102651f03a4SMatthew Barth     // Setting properties on interfaces to be served are optional
103651f03a4SMatthew Barth     if (jsonObj.contains("interfaces"))
104651f03a4SMatthew Barth     {
105651f03a4SMatthew Barth         setInterfaces(jsonObj);
106651f03a4SMatthew Barth     }
10714303a45SMatthew Barth }
108007de099SMatthew Barth 
enable()10914303a45SMatthew Barth void Zone::enable()
11014303a45SMatthew Barth {
111bc89a8a0SMatthew Barth     // Create thermal control dbus object
112bc89a8a0SMatthew Barth     _dbusZone = std::make_unique<DBusZone>(*this);
113a4483746SMatthew Barth 
114bc89a8a0SMatthew Barth     // Init all configured dbus interfaces' property states
115bc89a8a0SMatthew Barth     for (const auto& func : _propInitFunctions)
116bc89a8a0SMatthew Barth     {
117bc89a8a0SMatthew Barth         // Only call non-null init property functions
118bc89a8a0SMatthew Barth         if (func)
119bc89a8a0SMatthew Barth         {
120bc89a8a0SMatthew Barth             func(*_dbusZone, *this);
121bc89a8a0SMatthew Barth         }
122bc89a8a0SMatthew Barth     }
123bc89a8a0SMatthew Barth 
124bc89a8a0SMatthew Barth     // TODO - Restore any persisted properties in init function
125bc89a8a0SMatthew Barth     // Restore thermal control current mode state, if exists
126bc89a8a0SMatthew Barth     _dbusZone->restoreCurrentMode();
127bc89a8a0SMatthew Barth 
128bc89a8a0SMatthew Barth     // Emit object added for this zone's associated dbus object
129bc89a8a0SMatthew Barth     _dbusZone->emit_object_added();
130a4483746SMatthew Barth 
1312504c77cSMatthew Barth     // A decrease interval of 0sec disables the decrease timer
1322504c77cSMatthew Barth     if (_decInterval != std::chrono::seconds::zero())
1332504c77cSMatthew Barth     {
134007de099SMatthew Barth         // Start timer for fan target decreases
135007de099SMatthew Barth         _decTimer.restart(_decInterval);
1364f0d3b74SMatthew Barth     }
1372504c77cSMatthew Barth }
1384f0d3b74SMatthew Barth 
addFan(std::unique_ptr<Fan> fan)139de90fb4dSMatthew Barth void Zone::addFan(std::unique_ptr<Fan> fan)
140de90fb4dSMatthew Barth {
141de90fb4dSMatthew Barth     _fans.emplace_back(std::move(fan));
142de90fb4dSMatthew Barth }
143de90fb4dSMatthew Barth 
setTarget(uint64_t target)1448ba715e1SMatthew Barth void Zone::setTarget(uint64_t target)
1458ba715e1SMatthew Barth {
1465a2b5017SMatt Spinler     if (_isActive)
1478ba715e1SMatthew Barth     {
148a917e69aSMatt Spinler         if (_target != target)
149a917e69aSMatt Spinler         {
150a917e69aSMatt Spinler             FlightRecorder::instance().log(
151a917e69aSMatt Spinler                 "zone-set-target" + getName(),
152fbf4703fSPatrick Williams                 std::format("Set target {} (from {})", target, _target));
153a917e69aSMatt Spinler         }
1548ba715e1SMatthew Barth         _target = target;
1558ba715e1SMatthew Barth         for (auto& fan : _fans)
1568ba715e1SMatthew Barth         {
1578ba715e1SMatthew Barth             fan->setTarget(_target);
1588ba715e1SMatthew Barth         }
1598ba715e1SMatthew Barth     }
1608ba715e1SMatthew Barth }
1618ba715e1SMatthew Barth 
lockFanTarget(const std::string & fname,uint64_t target)162762e858bSMike Capps void Zone::lockFanTarget(const std::string& fname, uint64_t target)
163762e858bSMike Capps {
164*5e15c3baSPatrick Williams     auto fanItr = std::find_if(
165*5e15c3baSPatrick Williams         _fans.begin(), _fans.end(),
166*5e15c3baSPatrick Williams         [&fname](const auto& fan) { return fan->getName() == fname; });
167762e858bSMike Capps 
168762e858bSMike Capps     if (_fans.end() != fanItr)
169762e858bSMike Capps     {
170762e858bSMike Capps         (*fanItr)->lockTarget(target);
171762e858bSMike Capps     }
172762e858bSMike Capps     else
173762e858bSMike Capps     {
174762e858bSMike Capps         log<level::DEBUG>(
175fbf4703fSPatrick Williams             std::format("Configured fan {} not found in zone {} to lock target",
176762e858bSMike Capps                         fname, getName())
177762e858bSMike Capps                 .c_str());
178762e858bSMike Capps     }
179762e858bSMike Capps }
180762e858bSMike Capps 
unlockFanTarget(const std::string & fname,uint64_t target)181762e858bSMike Capps void Zone::unlockFanTarget(const std::string& fname, uint64_t target)
182762e858bSMike Capps {
183*5e15c3baSPatrick Williams     auto fanItr = std::find_if(
184*5e15c3baSPatrick Williams         _fans.begin(), _fans.end(),
185*5e15c3baSPatrick Williams         [&fname](const auto& fan) { return fan->getName() == fname; });
186762e858bSMike Capps 
187762e858bSMike Capps     if (_fans.end() != fanItr)
188762e858bSMike Capps     {
189762e858bSMike Capps         (*fanItr)->unlockTarget(target);
190762e858bSMike Capps 
191762e858bSMike Capps         // attempt to resume Zone target on fan
192762e858bSMike Capps         (*fanItr)->setTarget(getTarget());
193762e858bSMike Capps     }
194762e858bSMike Capps     else
195762e858bSMike Capps     {
196762e858bSMike Capps         log<level::DEBUG>(
197fbf4703fSPatrick Williams             std::format(
198762e858bSMike Capps                 "Configured fan {} not found in zone {} to unlock target",
199762e858bSMike Capps                 fname, getName())
200762e858bSMike Capps                 .c_str());
201762e858bSMike Capps     }
202762e858bSMike Capps }
203762e858bSMike Capps 
setTargetHold(const std::string & ident,uint64_t target,bool hold)2045368011eSMatthew Barth void Zone::setTargetHold(const std::string& ident, uint64_t target, bool hold)
2055368011eSMatthew Barth {
20698671610SMatt Spinler     using namespace std::string_literals;
20798671610SMatt Spinler 
2085368011eSMatthew Barth     if (!hold)
2095368011eSMatthew Barth     {
21098671610SMatt Spinler         size_t removed = _targetHolds.erase(ident);
21198671610SMatt Spinler         if (removed)
21298671610SMatt Spinler         {
21398671610SMatt Spinler             FlightRecorder::instance().log(
21498671610SMatt Spinler                 "zone-target"s + getName(),
215fbf4703fSPatrick Williams                 std::format("{} is removing target hold", ident));
21698671610SMatt Spinler         }
2175368011eSMatthew Barth     }
2185368011eSMatthew Barth     else
2195368011eSMatthew Barth     {
22098671610SMatt Spinler         if (!((_targetHolds.find(ident) != _targetHolds.end()) &&
22198671610SMatt Spinler               (_targetHolds[ident] == target)))
22298671610SMatt Spinler         {
22398671610SMatt Spinler             FlightRecorder::instance().log(
22498671610SMatt Spinler                 "zone-target"s + getName(),
225fbf4703fSPatrick Williams                 std::format("{} is setting target hold to {}", ident, target));
22698671610SMatt Spinler         }
22740554d8cSMatt Spinler         _targetHolds[ident] = target;
2285368011eSMatthew Barth         _isActive = false;
2295368011eSMatthew Barth     }
2305368011eSMatthew Barth 
23140554d8cSMatt Spinler     auto itHoldMax = std::max_element(_targetHolds.begin(), _targetHolds.end(),
2325368011eSMatthew Barth                                       [](const auto& aHold, const auto& bHold) {
2335368011eSMatthew Barth         return aHold.second < bHold.second;
2345368011eSMatthew Barth     });
23540554d8cSMatt Spinler     if (itHoldMax == _targetHolds.end())
2365368011eSMatthew Barth     {
2375368011eSMatthew Barth         _isActive = true;
2385368011eSMatthew Barth     }
2395368011eSMatthew Barth     else
2405368011eSMatthew Barth     {
24198671610SMatt Spinler         if (_target != itHoldMax->second)
24298671610SMatt Spinler         {
24398671610SMatt Spinler             FlightRecorder::instance().log(
24498671610SMatt Spinler                 "zone-target"s + getName(),
245fbf4703fSPatrick Williams                 std::format("Settings fans to target hold of {}",
24698671610SMatt Spinler                             itHoldMax->second));
24798671610SMatt Spinler         }
24898671610SMatt Spinler 
2495368011eSMatthew Barth         _target = itHoldMax->second;
2505368011eSMatthew Barth         for (auto& fan : _fans)
2515368011eSMatthew Barth         {
2525368011eSMatthew Barth             fan->setTarget(_target);
2535368011eSMatthew Barth         }
2545368011eSMatthew Barth     }
2555368011eSMatthew Barth }
2565368011eSMatthew Barth 
setFloorHold(const std::string & ident,uint64_t target,bool hold)25740554d8cSMatt Spinler void Zone::setFloorHold(const std::string& ident, uint64_t target, bool hold)
25840554d8cSMatt Spinler {
25940554d8cSMatt Spinler     using namespace std::string_literals;
26040554d8cSMatt Spinler 
261dc3152d3SMatthew Barth     if (target > _ceiling)
262dc3152d3SMatthew Barth     {
263dc3152d3SMatthew Barth         target = _ceiling;
264dc3152d3SMatthew Barth     }
265dc3152d3SMatthew Barth 
26640554d8cSMatt Spinler     if (!hold)
26740554d8cSMatt Spinler     {
26840554d8cSMatt Spinler         size_t removed = _floorHolds.erase(ident);
26940554d8cSMatt Spinler         if (removed)
27040554d8cSMatt Spinler         {
27140554d8cSMatt Spinler             FlightRecorder::instance().log(
27240554d8cSMatt Spinler                 "zone-floor"s + getName(),
273fbf4703fSPatrick Williams                 std::format("{} is removing floor hold", ident));
27440554d8cSMatt Spinler         }
27540554d8cSMatt Spinler     }
27640554d8cSMatt Spinler     else
27740554d8cSMatt Spinler     {
27840554d8cSMatt Spinler         if (!((_floorHolds.find(ident) != _floorHolds.end()) &&
27940554d8cSMatt Spinler               (_floorHolds[ident] == target)))
28040554d8cSMatt Spinler         {
28140554d8cSMatt Spinler             FlightRecorder::instance().log(
28240554d8cSMatt Spinler                 "zone-floor"s + getName(),
283fbf4703fSPatrick Williams                 std::format("{} is setting floor hold to {}", ident, target));
28440554d8cSMatt Spinler         }
28540554d8cSMatt Spinler         _floorHolds[ident] = target;
28640554d8cSMatt Spinler     }
28740554d8cSMatt Spinler 
28840554d8cSMatt Spinler     if (!std::all_of(_floorChange.begin(), _floorChange.end(),
28940554d8cSMatt Spinler                      [](const auto& entry) { return entry.second; }))
29040554d8cSMatt Spinler     {
29140554d8cSMatt Spinler         return;
29240554d8cSMatt Spinler     }
29340554d8cSMatt Spinler 
29440554d8cSMatt Spinler     auto itHoldMax = std::max_element(_floorHolds.begin(), _floorHolds.end(),
29540554d8cSMatt Spinler                                       [](const auto& aHold, const auto& bHold) {
29640554d8cSMatt Spinler         return aHold.second < bHold.second;
29740554d8cSMatt Spinler     });
29840554d8cSMatt Spinler     if (itHoldMax == _floorHolds.end())
29940554d8cSMatt Spinler     {
30040554d8cSMatt Spinler         if (_floor != _defaultFloor)
30140554d8cSMatt Spinler         {
30240554d8cSMatt Spinler             FlightRecorder::instance().log(
30340554d8cSMatt Spinler                 "zone-floor"s + getName(),
304fbf4703fSPatrick Williams                 std::format("No set floor exists, using default floor",
30540554d8cSMatt Spinler                             _defaultFloor));
30640554d8cSMatt Spinler         }
30740554d8cSMatt Spinler         _floor = _defaultFloor;
30840554d8cSMatt Spinler     }
30940554d8cSMatt Spinler     else
31040554d8cSMatt Spinler     {
31140554d8cSMatt Spinler         if (_floor != itHoldMax->second)
31240554d8cSMatt Spinler         {
31340554d8cSMatt Spinler             FlightRecorder::instance().log(
31440554d8cSMatt Spinler                 "zone-floor"s + getName(),
315fbf4703fSPatrick Williams                 std::format("Setting new floor to {}", itHoldMax->second));
31640554d8cSMatt Spinler         }
31740554d8cSMatt Spinler         _floor = itHoldMax->second;
31840554d8cSMatt Spinler     }
31940554d8cSMatt Spinler 
32040554d8cSMatt Spinler     // Floor above target, update target to floor
32140554d8cSMatt Spinler     if (_target < _floor)
32240554d8cSMatt Spinler     {
32340554d8cSMatt Spinler         requestIncrease(_floor - _target);
32440554d8cSMatt Spinler     }
32540554d8cSMatt Spinler }
32640554d8cSMatt Spinler 
setFloor(uint64_t target)32712cb125aSMatthew Barth void Zone::setFloor(uint64_t target)
32812cb125aSMatthew Barth {
32912cb125aSMatthew Barth     // Check all entries are set to allow floor to be set
3308ba715e1SMatthew Barth     auto pred = [](const auto& entry) { return entry.second; };
33112cb125aSMatthew Barth     if (std::all_of(_floorChange.begin(), _floorChange.end(), pred))
33212cb125aSMatthew Barth     {
333dc3152d3SMatthew Barth         _floor = (target > _ceiling) ? _ceiling : target;
33412cb125aSMatthew Barth         // Floor above target, update target to floor
33512cb125aSMatthew Barth         if (_target < _floor)
33612cb125aSMatthew Barth         {
33712cb125aSMatthew Barth             requestIncrease(_floor - _target);
33812cb125aSMatthew Barth         }
33912cb125aSMatthew Barth     }
34012cb125aSMatthew Barth }
34112cb125aSMatthew Barth 
requestIncrease(uint64_t targetDelta)34212cb125aSMatthew Barth void Zone::requestIncrease(uint64_t targetDelta)
34312cb125aSMatthew Barth {
3442b3253e3SMatthew Barth     // Only increase when delta is higher than the current increase delta for
3452b3253e3SMatthew Barth     // the zone and currently under ceiling
3462b3253e3SMatthew Barth     if (targetDelta > _incDelta && _target < _ceiling)
3472b3253e3SMatthew Barth     {
3482b3253e3SMatthew Barth         auto requestTarget = getRequestTargetBase();
3492b3253e3SMatthew Barth         requestTarget = (targetDelta - _incDelta) + requestTarget;
3502b3253e3SMatthew Barth         _incDelta = targetDelta;
3512b3253e3SMatthew Barth         // Target can not go above a current ceiling
3522b3253e3SMatthew Barth         if (requestTarget > _ceiling)
3532b3253e3SMatthew Barth         {
3542b3253e3SMatthew Barth             requestTarget = _ceiling;
3552b3253e3SMatthew Barth         }
3568ba715e1SMatthew Barth         setTarget(requestTarget);
357007de099SMatthew Barth         // Restart timer countdown for fan target increase
358007de099SMatthew Barth         _incTimer.restartOnce(_incDelay);
3592b3253e3SMatthew Barth     }
36012cb125aSMatthew Barth }
36112cb125aSMatthew Barth 
incTimerExpired()362007de099SMatthew Barth void Zone::incTimerExpired()
363007de099SMatthew Barth {
364007de099SMatthew Barth     // Clear increase delta when timer expires allowing additional target
365007de099SMatthew Barth     // increase requests or target decreases to occur
366007de099SMatthew Barth     _incDelta = 0;
367007de099SMatthew Barth }
368007de099SMatthew Barth 
requestDecrease(uint64_t targetDelta)36945c44ea0SMatthew Barth void Zone::requestDecrease(uint64_t targetDelta)
37045c44ea0SMatthew Barth {
37145c44ea0SMatthew Barth     // Only decrease the lowest target delta requested
37245c44ea0SMatthew Barth     if (_decDelta == 0 || targetDelta < _decDelta)
37345c44ea0SMatthew Barth     {
37445c44ea0SMatthew Barth         _decDelta = targetDelta;
37545c44ea0SMatthew Barth     }
37645c44ea0SMatthew Barth }
37745c44ea0SMatthew Barth 
decTimerExpired()378007de099SMatthew Barth void Zone::decTimerExpired()
379007de099SMatthew Barth {
380007de099SMatthew Barth     // Check all entries are set to allow a decrease
38161b73296SPatrick Williams     auto pred = [](const auto& entry) { return entry.second; };
382007de099SMatthew Barth     auto decAllowed = std::all_of(_decAllowed.begin(), _decAllowed.end(), pred);
383007de099SMatthew Barth 
384007de099SMatthew Barth     // Only decrease targets when allowed, a requested decrease target delta
385007de099SMatthew Barth     // exists, where no requested increases exist and the increase timer is not
386007de099SMatthew Barth     // running (i.e. not in the middle of increasing)
387007de099SMatthew Barth     if (decAllowed && _decDelta != 0 && _incDelta == 0 &&
388007de099SMatthew Barth         !_incTimer.isEnabled())
389007de099SMatthew Barth     {
390007de099SMatthew Barth         auto requestTarget = getRequestTargetBase();
391007de099SMatthew Barth         // Request target should not start above ceiling
392007de099SMatthew Barth         if (requestTarget > _ceiling)
393007de099SMatthew Barth         {
394007de099SMatthew Barth             requestTarget = _ceiling;
395007de099SMatthew Barth         }
396007de099SMatthew Barth         // Target can not go below the defined floor
397007de099SMatthew Barth         if ((requestTarget < _decDelta) || (requestTarget - _decDelta < _floor))
398007de099SMatthew Barth         {
399007de099SMatthew Barth             requestTarget = _floor;
400007de099SMatthew Barth         }
401007de099SMatthew Barth         else
402007de099SMatthew Barth         {
403007de099SMatthew Barth             requestTarget = requestTarget - _decDelta;
404007de099SMatthew Barth         }
405007de099SMatthew Barth         setTarget(requestTarget);
406007de099SMatthew Barth     }
407007de099SMatthew Barth     // Clear decrease delta when timer expires
408007de099SMatthew Barth     _decDelta = 0;
409007de099SMatthew Barth     // Decrease timer is restarted since its repeating
410007de099SMatthew Barth }
411007de099SMatthew Barth 
setPersisted(const std::string & intf,const std::string & prop)412a0dd1350SMatthew Barth void Zone::setPersisted(const std::string& intf, const std::string& prop)
413a0dd1350SMatthew Barth {
414a0dd1350SMatthew Barth     if (std::find_if(_propsPersisted[intf].begin(), _propsPersisted[intf].end(),
415bc89a8a0SMatthew Barth                      [&prop](const auto& p) { return prop == p; }) ==
416a0dd1350SMatthew Barth         _propsPersisted[intf].end())
417a0dd1350SMatthew Barth     {
418a0dd1350SMatthew Barth         _propsPersisted[intf].emplace_back(prop);
419a0dd1350SMatthew Barth     }
420a0dd1350SMatthew Barth }
421a0dd1350SMatthew Barth 
isPersisted(const std::string & intf,const std::string & prop) const422279183feSMatthew Barth bool Zone::isPersisted(const std::string& intf, const std::string& prop) const
423279183feSMatthew Barth {
424279183feSMatthew Barth     auto it = _propsPersisted.find(intf);
425279183feSMatthew Barth     if (it == _propsPersisted.end())
426279183feSMatthew Barth     {
427279183feSMatthew Barth         return false;
428279183feSMatthew Barth     }
429279183feSMatthew Barth 
430279183feSMatthew Barth     return std::any_of(it->second.begin(), it->second.end(),
431279183feSMatthew Barth                        [&prop](const auto& p) { return prop == p; });
432279183feSMatthew Barth }
433279183feSMatthew Barth 
setPowerOnTarget(const json & jsonObj)434ab8e4b82SMatthew Barth void Zone::setPowerOnTarget(const json& jsonObj)
4354f0d3b74SMatthew Barth {
43612e888fbSMatthew Barth     if (!jsonObj.contains("poweron_target"))
4374f0d3b74SMatthew Barth     {
438ab8e4b82SMatthew Barth         auto msg = "Missing required zone's poweron target";
439ab8e4b82SMatthew Barth         log<level::ERR>(msg, entry("JSON=%s", jsonObj.dump().c_str()));
440ab8e4b82SMatthew Barth         throw std::runtime_error(msg);
4414f0d3b74SMatthew Barth     }
442ab8e4b82SMatthew Barth     _poweronTarget = jsonObj["poweron_target"].get<uint64_t>();
4434f0d3b74SMatthew Barth }
4444f0d3b74SMatthew Barth 
setInterfaces(const json & jsonObj)445651f03a4SMatthew Barth void Zone::setInterfaces(const json& jsonObj)
446651f03a4SMatthew Barth {
447651f03a4SMatthew Barth     for (const auto& interface : jsonObj["interfaces"])
448651f03a4SMatthew Barth     {
449651f03a4SMatthew Barth         if (!interface.contains("name") || !interface.contains("properties"))
450651f03a4SMatthew Barth         {
451651f03a4SMatthew Barth             log<level::ERR>("Missing required zone interface attributes",
452651f03a4SMatthew Barth                             entry("JSON=%s", interface.dump().c_str()));
453651f03a4SMatthew Barth             throw std::runtime_error(
454651f03a4SMatthew Barth                 "Missing required zone interface attributes");
455651f03a4SMatthew Barth         }
456216229c1SMatthew Barth         auto propFuncs =
457216229c1SMatthew Barth             _intfPropHandlers.find(interface["name"].get<std::string>());
458216229c1SMatthew Barth         if (propFuncs == _intfPropHandlers.end())
459216229c1SMatthew Barth         {
460216229c1SMatthew Barth             // Construct list of available configurable interfaces
461*5e15c3baSPatrick Williams             auto intfs = std::accumulate(
462*5e15c3baSPatrick Williams                 std::next(_intfPropHandlers.begin()), _intfPropHandlers.end(),
463*5e15c3baSPatrick Williams                 _intfPropHandlers.begin()->first, [](auto list, auto intf) {
464216229c1SMatthew Barth                 return std::move(list) + ", " + intf.first;
465216229c1SMatthew Barth             });
466216229c1SMatthew Barth             log<level::ERR>("Configured interface not available",
467216229c1SMatthew Barth                             entry("JSON=%s", interface.dump().c_str()),
468216229c1SMatthew Barth                             entry("AVAILABLE_INTFS=%s", intfs.c_str()));
469216229c1SMatthew Barth             throw std::runtime_error("Configured interface not available");
470216229c1SMatthew Barth         }
471216229c1SMatthew Barth 
472651f03a4SMatthew Barth         for (const auto& property : interface["properties"])
473651f03a4SMatthew Barth         {
474651f03a4SMatthew Barth             if (!property.contains("name"))
475651f03a4SMatthew Barth             {
476651f03a4SMatthew Barth                 log<level::ERR>(
477651f03a4SMatthew Barth                     "Missing required interface property attributes",
478651f03a4SMatthew Barth                     entry("JSON=%s", property.dump().c_str()));
479651f03a4SMatthew Barth                 throw std::runtime_error(
480651f03a4SMatthew Barth                     "Missing required interface property attributes");
481651f03a4SMatthew Barth             }
482651f03a4SMatthew Barth             // Attribute "persist" is optional, defaults to `false`
483651f03a4SMatthew Barth             auto persist = false;
484651f03a4SMatthew Barth             if (property.contains("persist"))
485651f03a4SMatthew Barth             {
486651f03a4SMatthew Barth                 persist = property["persist"].get<bool>();
487651f03a4SMatthew Barth             }
488651f03a4SMatthew Barth             // Property name from JSON must exactly match supported
489651f03a4SMatthew Barth             // index names to functions in property namespace
490216229c1SMatthew Barth             auto propFunc =
491216229c1SMatthew Barth                 propFuncs->second.find(property["name"].get<std::string>());
492216229c1SMatthew Barth             if (propFunc == propFuncs->second.end())
493651f03a4SMatthew Barth             {
494216229c1SMatthew Barth                 // Construct list of available configurable properties
495651f03a4SMatthew Barth                 auto props = std::accumulate(
496216229c1SMatthew Barth                     std::next(propFuncs->second.begin()),
497216229c1SMatthew Barth                     propFuncs->second.end(), propFuncs->second.begin()->first,
498216229c1SMatthew Barth                     [](auto list, auto prop) {
499651f03a4SMatthew Barth                     return std::move(list) + ", " + prop.first;
500651f03a4SMatthew Barth                 });
501216229c1SMatthew Barth                 log<level::ERR>("Configured property not available",
502651f03a4SMatthew Barth                                 entry("JSON=%s", property.dump().c_str()),
503651f03a4SMatthew Barth                                 entry("AVAILABLE_PROPS=%s", props.c_str()));
504651f03a4SMatthew Barth                 throw std::runtime_error(
505651f03a4SMatthew Barth                     "Configured property function not available");
506651f03a4SMatthew Barth             }
507651f03a4SMatthew Barth 
508bc89a8a0SMatthew Barth             _propInitFunctions.emplace_back(
509bc89a8a0SMatthew Barth                 propFunc->second(property, persist));
510a4483746SMatthew Barth         }
511a4483746SMatthew Barth     }
512a4483746SMatthew Barth }
513a4483746SMatthew Barth 
dump() const5149db6dd1dSMatt Spinler json Zone::dump() const
5159db6dd1dSMatt Spinler {
5169db6dd1dSMatt Spinler     json output;
5179db6dd1dSMatt Spinler 
5189db6dd1dSMatt Spinler     output["active"] = _isActive;
5199db6dd1dSMatt Spinler     output["floor"] = _floor;
520077448a9SMatthew Barth     output["ceiling"] = _ceiling;
5219db6dd1dSMatt Spinler     output["target"] = _target;
5229db6dd1dSMatt Spinler     output["increase_delta"] = _incDelta;
5239db6dd1dSMatt Spinler     output["decrease_delta"] = _decDelta;
5249db6dd1dSMatt Spinler     output["power_on_target"] = _poweronTarget;
5259db6dd1dSMatt Spinler     output["default_ceiling"] = _defaultCeiling;
5269db6dd1dSMatt Spinler     output["default_floor"] = _defaultFloor;
5279db6dd1dSMatt Spinler     output["increase_delay"] = _incDelay.count();
5289db6dd1dSMatt Spinler     output["decrease_interval"] = _decInterval.count();
5299db6dd1dSMatt Spinler     output["requested_target_base"] = _requestTargetBase;
5309db6dd1dSMatt Spinler     output["floor_change"] = _floorChange;
5319db6dd1dSMatt Spinler     output["decrease_allowed"] = _decAllowed;
5329db6dd1dSMatt Spinler     output["persisted_props"] = _propsPersisted;
53340554d8cSMatt Spinler     output["target_holds"] = _targetHolds;
53440554d8cSMatt Spinler     output["floor_holds"] = _floorHolds;
5359db6dd1dSMatt Spinler 
5362541f99cSMatt Spinler     std::map<std::string, std::vector<uint64_t>> lockedTargets;
5372541f99cSMatt Spinler     for (const auto& fan : _fans)
5382541f99cSMatt Spinler     {
5392541f99cSMatt Spinler         const auto& locks = fan->getLockedTargets();
5402541f99cSMatt Spinler         if (!locks.empty())
5412541f99cSMatt Spinler         {
5422541f99cSMatt Spinler             lockedTargets[fan->getName()] = locks;
5432541f99cSMatt Spinler         }
5442541f99cSMatt Spinler     }
5452541f99cSMatt Spinler     output["target_locks"] = lockedTargets;
5462541f99cSMatt Spinler 
5479db6dd1dSMatt Spinler     return output;
5489db6dd1dSMatt Spinler }
5499db6dd1dSMatt Spinler 
550651f03a4SMatthew Barth /**
551216229c1SMatthew Barth  * Properties of interfaces supported by the zone configuration that return
552ab8e4b82SMatthew Barth  * a handler function that sets the zone's property value(s) and persist
553ab8e4b82SMatthew Barth  * state.
554651f03a4SMatthew Barth  */
555651f03a4SMatthew Barth namespace zone::property
556651f03a4SMatthew Barth {
557b584d818SMatthew Barth // Get a set property handler function for the configured values of the
558b584d818SMatthew Barth // "Supported" property
supported(const json & jsonObj,bool persist)559bc89a8a0SMatthew Barth std::function<void(DBusZone&, Zone&)> supported(const json& jsonObj,
560bc89a8a0SMatthew Barth                                                 bool persist)
561651f03a4SMatthew Barth {
562651f03a4SMatthew Barth     std::vector<std::string> values;
563216229c1SMatthew Barth     if (!jsonObj.contains("values"))
564216229c1SMatthew Barth     {
565ab8e4b82SMatthew Barth         log<level::ERR>("No 'values' found for \"Supported\" property, "
566ab8e4b82SMatthew Barth                         "using an empty list",
567216229c1SMatthew Barth                         entry("JSON=%s", jsonObj.dump().c_str()));
568216229c1SMatthew Barth     }
569216229c1SMatthew Barth     else
570216229c1SMatthew Barth     {
571651f03a4SMatthew Barth         for (const auto& value : jsonObj["values"])
572651f03a4SMatthew Barth         {
573216229c1SMatthew Barth             if (!value.contains("value"))
574216229c1SMatthew Barth             {
575216229c1SMatthew Barth                 log<level::ERR>("No 'value' found for \"Supported\" property "
576216229c1SMatthew Barth                                 "entry, skipping",
577216229c1SMatthew Barth                                 entry("JSON=%s", value.dump().c_str()));
578216229c1SMatthew Barth             }
579216229c1SMatthew Barth             else
580216229c1SMatthew Barth             {
581651f03a4SMatthew Barth                 values.emplace_back(value["value"].get<std::string>());
582651f03a4SMatthew Barth             }
583216229c1SMatthew Barth         }
584651f03a4SMatthew Barth     }
585651f03a4SMatthew Barth 
586b584d818SMatthew Barth     return Zone::setProperty<std::vector<std::string>>(
587bc89a8a0SMatthew Barth         DBusZone::thermalModeIntf, DBusZone::supportedProp,
588bc89a8a0SMatthew Barth         &DBusZone::supported, std::move(values), persist);
589216229c1SMatthew Barth }
590216229c1SMatthew Barth 
591ab8e4b82SMatthew Barth // Get a set property handler function for a configured value of the
592ab8e4b82SMatthew Barth // "Current" property
current(const json & jsonObj,bool persist)593bc89a8a0SMatthew Barth std::function<void(DBusZone&, Zone&)> current(const json& jsonObj, bool persist)
594651f03a4SMatthew Barth {
595216229c1SMatthew Barth     // Use default value for "Current" property if no "value" entry given
596216229c1SMatthew Barth     if (!jsonObj.contains("value"))
597216229c1SMatthew Barth     {
598b584d818SMatthew Barth         log<level::INFO>("No 'value' found for \"Current\" property, "
599216229c1SMatthew Barth                          "using default",
600216229c1SMatthew Barth                          entry("JSON=%s", jsonObj.dump().c_str()));
601b584d818SMatthew Barth         // Set persist state of property
602bc89a8a0SMatthew Barth         return Zone::setPropertyPersist(DBusZone::thermalModeIntf,
603bc89a8a0SMatthew Barth                                         DBusZone::currentProp, persist);
604216229c1SMatthew Barth     }
605216229c1SMatthew Barth 
606b584d818SMatthew Barth     return Zone::setProperty<std::string>(
607bc89a8a0SMatthew Barth         DBusZone::thermalModeIntf, DBusZone::currentProp, &DBusZone::current,
608b584d818SMatthew Barth         jsonObj["value"].get<std::string>(), persist);
609651f03a4SMatthew Barth }
610b584d818SMatthew Barth 
611651f03a4SMatthew Barth } // namespace zone::property
612651f03a4SMatthew Barth 
6134f0d3b74SMatthew Barth } // namespace phosphor::fan::control::json
614