14f0d3b74SMatthew Barth /**
24f0d3b74SMatthew Barth  * Copyright © 2020 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 
18*40554d8cSMatt 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 
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>();
81ab8e4b82SMatthew Barth         // Start with the current floor set as the default
82ab8e4b82SMatthew Barth         _floor = _defaultFloor;
83ab8e4b82SMatthew Barth     }
84ab8e4b82SMatthew Barth 
852504c77cSMatthew Barth     // Decrease interval is optional, defaults to 0
862504c77cSMatthew Barth     // A decrease interval of 0sec disables the decrease timer
872504c77cSMatthew Barth     if (jsonObj.contains("decrease_interval"))
882504c77cSMatthew Barth     {
892504c77cSMatthew Barth         _decInterval =
902504c77cSMatthew Barth             std::chrono::seconds(jsonObj["decrease_interval"].get<uint64_t>());
912504c77cSMatthew Barth     }
92ab8e4b82SMatthew Barth 
93651f03a4SMatthew Barth     // Setting properties on interfaces to be served are optional
94651f03a4SMatthew Barth     if (jsonObj.contains("interfaces"))
95651f03a4SMatthew Barth     {
96651f03a4SMatthew Barth         setInterfaces(jsonObj);
97651f03a4SMatthew Barth     }
9814303a45SMatthew Barth }
99007de099SMatthew Barth 
10014303a45SMatthew Barth void Zone::enable()
10114303a45SMatthew Barth {
102bc89a8a0SMatthew Barth     // Create thermal control dbus object
103bc89a8a0SMatthew Barth     _dbusZone = std::make_unique<DBusZone>(*this);
104a4483746SMatthew Barth 
105bc89a8a0SMatthew Barth     // Init all configured dbus interfaces' property states
106bc89a8a0SMatthew Barth     for (const auto& func : _propInitFunctions)
107bc89a8a0SMatthew Barth     {
108bc89a8a0SMatthew Barth         // Only call non-null init property functions
109bc89a8a0SMatthew Barth         if (func)
110bc89a8a0SMatthew Barth         {
111bc89a8a0SMatthew Barth             func(*_dbusZone, *this);
112bc89a8a0SMatthew Barth         }
113bc89a8a0SMatthew Barth     }
114bc89a8a0SMatthew Barth 
115bc89a8a0SMatthew Barth     // TODO - Restore any persisted properties in init function
116bc89a8a0SMatthew Barth     // Restore thermal control current mode state, if exists
117bc89a8a0SMatthew Barth     _dbusZone->restoreCurrentMode();
118bc89a8a0SMatthew Barth 
119bc89a8a0SMatthew Barth     // Emit object added for this zone's associated dbus object
120bc89a8a0SMatthew Barth     _dbusZone->emit_object_added();
121a4483746SMatthew Barth 
1222504c77cSMatthew Barth     // A decrease interval of 0sec disables the decrease timer
1232504c77cSMatthew Barth     if (_decInterval != std::chrono::seconds::zero())
1242504c77cSMatthew Barth     {
125007de099SMatthew Barth         // Start timer for fan target decreases
126007de099SMatthew Barth         _decTimer.restart(_decInterval);
1274f0d3b74SMatthew Barth     }
1282504c77cSMatthew Barth }
1294f0d3b74SMatthew Barth 
130de90fb4dSMatthew Barth void Zone::addFan(std::unique_ptr<Fan> fan)
131de90fb4dSMatthew Barth {
132de90fb4dSMatthew Barth     _fans.emplace_back(std::move(fan));
133de90fb4dSMatthew Barth }
134de90fb4dSMatthew Barth 
1358ba715e1SMatthew Barth void Zone::setTarget(uint64_t target)
1368ba715e1SMatthew Barth {
1375a2b5017SMatt Spinler     if (_isActive)
1388ba715e1SMatthew Barth     {
1398ba715e1SMatthew Barth         _target = target;
1408ba715e1SMatthew Barth         for (auto& fan : _fans)
1418ba715e1SMatthew Barth         {
1428ba715e1SMatthew Barth             fan->setTarget(_target);
1438ba715e1SMatthew Barth         }
1448ba715e1SMatthew Barth     }
1458ba715e1SMatthew Barth }
1468ba715e1SMatthew Barth 
1475368011eSMatthew Barth void Zone::setTargetHold(const std::string& ident, uint64_t target, bool hold)
1485368011eSMatthew Barth {
1495368011eSMatthew Barth     if (!hold)
1505368011eSMatthew Barth     {
151*40554d8cSMatt Spinler         _targetHolds.erase(ident);
1525368011eSMatthew Barth     }
1535368011eSMatthew Barth     else
1545368011eSMatthew Barth     {
155*40554d8cSMatt Spinler         _targetHolds[ident] = target;
1565368011eSMatthew Barth         _isActive = false;
1575368011eSMatthew Barth     }
1585368011eSMatthew Barth 
159*40554d8cSMatt Spinler     auto itHoldMax = std::max_element(_targetHolds.begin(), _targetHolds.end(),
1605368011eSMatthew Barth                                       [](const auto& aHold, const auto& bHold) {
1615368011eSMatthew Barth                                           return aHold.second < bHold.second;
1625368011eSMatthew Barth                                       });
163*40554d8cSMatt Spinler     if (itHoldMax == _targetHolds.end())
1645368011eSMatthew Barth     {
1655368011eSMatthew Barth         _isActive = true;
1665368011eSMatthew Barth     }
1675368011eSMatthew Barth     else
1685368011eSMatthew Barth     {
1695368011eSMatthew Barth         _target = itHoldMax->second;
1705368011eSMatthew Barth         for (auto& fan : _fans)
1715368011eSMatthew Barth         {
1725368011eSMatthew Barth             fan->setTarget(_target);
1735368011eSMatthew Barth         }
1745368011eSMatthew Barth     }
1755368011eSMatthew Barth }
1765368011eSMatthew Barth 
177*40554d8cSMatt Spinler void Zone::setFloorHold(const std::string& ident, uint64_t target, bool hold)
178*40554d8cSMatt Spinler {
179*40554d8cSMatt Spinler     using namespace std::string_literals;
180*40554d8cSMatt Spinler 
181*40554d8cSMatt Spinler     if (!hold)
182*40554d8cSMatt Spinler     {
183*40554d8cSMatt Spinler         size_t removed = _floorHolds.erase(ident);
184*40554d8cSMatt Spinler         if (removed)
185*40554d8cSMatt Spinler         {
186*40554d8cSMatt Spinler             FlightRecorder::instance().log(
187*40554d8cSMatt Spinler                 "zone-floor"s + getName(),
188*40554d8cSMatt Spinler                 fmt::format("{} is removing floor hold", ident));
189*40554d8cSMatt Spinler         }
190*40554d8cSMatt Spinler     }
191*40554d8cSMatt Spinler     else
192*40554d8cSMatt Spinler     {
193*40554d8cSMatt Spinler         if (!((_floorHolds.find(ident) != _floorHolds.end()) &&
194*40554d8cSMatt Spinler               (_floorHolds[ident] == target)))
195*40554d8cSMatt Spinler         {
196*40554d8cSMatt Spinler             FlightRecorder::instance().log(
197*40554d8cSMatt Spinler                 "zone-floor"s + getName(),
198*40554d8cSMatt Spinler                 fmt::format("{} is setting floor hold to {}", ident, target));
199*40554d8cSMatt Spinler         }
200*40554d8cSMatt Spinler         _floorHolds[ident] = target;
201*40554d8cSMatt Spinler     }
202*40554d8cSMatt Spinler 
203*40554d8cSMatt Spinler     if (!std::all_of(_floorChange.begin(), _floorChange.end(),
204*40554d8cSMatt Spinler                      [](const auto& entry) { return entry.second; }))
205*40554d8cSMatt Spinler     {
206*40554d8cSMatt Spinler         return;
207*40554d8cSMatt Spinler     }
208*40554d8cSMatt Spinler 
209*40554d8cSMatt Spinler     auto itHoldMax = std::max_element(_floorHolds.begin(), _floorHolds.end(),
210*40554d8cSMatt Spinler                                       [](const auto& aHold, const auto& bHold) {
211*40554d8cSMatt Spinler                                           return aHold.second < bHold.second;
212*40554d8cSMatt Spinler                                       });
213*40554d8cSMatt Spinler     if (itHoldMax == _floorHolds.end())
214*40554d8cSMatt Spinler     {
215*40554d8cSMatt Spinler         if (_floor != _defaultFloor)
216*40554d8cSMatt Spinler         {
217*40554d8cSMatt Spinler             FlightRecorder::instance().log(
218*40554d8cSMatt Spinler                 "zone-floor"s + getName(),
219*40554d8cSMatt Spinler                 fmt::format("No set floor exists, using default floor",
220*40554d8cSMatt Spinler                             _defaultFloor));
221*40554d8cSMatt Spinler         }
222*40554d8cSMatt Spinler         _floor = _defaultFloor;
223*40554d8cSMatt Spinler     }
224*40554d8cSMatt Spinler     else
225*40554d8cSMatt Spinler     {
226*40554d8cSMatt Spinler         if (_floor != itHoldMax->second)
227*40554d8cSMatt Spinler         {
228*40554d8cSMatt Spinler             FlightRecorder::instance().log(
229*40554d8cSMatt Spinler                 "zone-floor"s + getName(),
230*40554d8cSMatt Spinler                 fmt::format("Setting new floor to {}", itHoldMax->second));
231*40554d8cSMatt Spinler         }
232*40554d8cSMatt Spinler         _floor = itHoldMax->second;
233*40554d8cSMatt Spinler     }
234*40554d8cSMatt Spinler 
235*40554d8cSMatt Spinler     // Floor above target, update target to floor
236*40554d8cSMatt Spinler     if (_target < _floor)
237*40554d8cSMatt Spinler     {
238*40554d8cSMatt Spinler         requestIncrease(_floor - _target);
239*40554d8cSMatt Spinler     }
240*40554d8cSMatt Spinler }
241*40554d8cSMatt Spinler 
24212cb125aSMatthew Barth void Zone::setFloor(uint64_t target)
24312cb125aSMatthew Barth {
24412cb125aSMatthew Barth     // Check all entries are set to allow floor to be set
2458ba715e1SMatthew Barth     auto pred = [](const auto& entry) { return entry.second; };
24612cb125aSMatthew Barth     if (std::all_of(_floorChange.begin(), _floorChange.end(), pred))
24712cb125aSMatthew Barth     {
24812cb125aSMatthew Barth         _floor = target;
24912cb125aSMatthew Barth         // Floor above target, update target to floor
25012cb125aSMatthew Barth         if (_target < _floor)
25112cb125aSMatthew Barth         {
25212cb125aSMatthew Barth             requestIncrease(_floor - _target);
25312cb125aSMatthew Barth         }
25412cb125aSMatthew Barth     }
25512cb125aSMatthew Barth }
25612cb125aSMatthew Barth 
25712cb125aSMatthew Barth void Zone::requestIncrease(uint64_t targetDelta)
25812cb125aSMatthew Barth {
2592b3253e3SMatthew Barth     // Only increase when delta is higher than the current increase delta for
2602b3253e3SMatthew Barth     // the zone and currently under ceiling
2612b3253e3SMatthew Barth     if (targetDelta > _incDelta && _target < _ceiling)
2622b3253e3SMatthew Barth     {
2632b3253e3SMatthew Barth         auto requestTarget = getRequestTargetBase();
2642b3253e3SMatthew Barth         requestTarget = (targetDelta - _incDelta) + requestTarget;
2652b3253e3SMatthew Barth         _incDelta = targetDelta;
2662b3253e3SMatthew Barth         // Target can not go above a current ceiling
2672b3253e3SMatthew Barth         if (requestTarget > _ceiling)
2682b3253e3SMatthew Barth         {
2692b3253e3SMatthew Barth             requestTarget = _ceiling;
2702b3253e3SMatthew Barth         }
2718ba715e1SMatthew Barth         setTarget(requestTarget);
272007de099SMatthew Barth         // Restart timer countdown for fan target increase
273007de099SMatthew Barth         _incTimer.restartOnce(_incDelay);
2742b3253e3SMatthew Barth     }
27512cb125aSMatthew Barth }
27612cb125aSMatthew Barth 
277007de099SMatthew Barth void Zone::incTimerExpired()
278007de099SMatthew Barth {
279007de099SMatthew Barth     // Clear increase delta when timer expires allowing additional target
280007de099SMatthew Barth     // increase requests or target decreases to occur
281007de099SMatthew Barth     _incDelta = 0;
282007de099SMatthew Barth }
283007de099SMatthew Barth 
28445c44ea0SMatthew Barth void Zone::requestDecrease(uint64_t targetDelta)
28545c44ea0SMatthew Barth {
28645c44ea0SMatthew Barth     // Only decrease the lowest target delta requested
28745c44ea0SMatthew Barth     if (_decDelta == 0 || targetDelta < _decDelta)
28845c44ea0SMatthew Barth     {
28945c44ea0SMatthew Barth         _decDelta = targetDelta;
29045c44ea0SMatthew Barth     }
29145c44ea0SMatthew Barth }
29245c44ea0SMatthew Barth 
293007de099SMatthew Barth void Zone::decTimerExpired()
294007de099SMatthew Barth {
295007de099SMatthew Barth     // Check all entries are set to allow a decrease
296007de099SMatthew Barth     auto pred = [](auto const& entry) { return entry.second; };
297007de099SMatthew Barth     auto decAllowed = std::all_of(_decAllowed.begin(), _decAllowed.end(), pred);
298007de099SMatthew Barth 
299007de099SMatthew Barth     // Only decrease targets when allowed, a requested decrease target delta
300007de099SMatthew Barth     // exists, where no requested increases exist and the increase timer is not
301007de099SMatthew Barth     // running (i.e. not in the middle of increasing)
302007de099SMatthew Barth     if (decAllowed && _decDelta != 0 && _incDelta == 0 &&
303007de099SMatthew Barth         !_incTimer.isEnabled())
304007de099SMatthew Barth     {
305007de099SMatthew Barth         auto requestTarget = getRequestTargetBase();
306007de099SMatthew Barth         // Request target should not start above ceiling
307007de099SMatthew Barth         if (requestTarget > _ceiling)
308007de099SMatthew Barth         {
309007de099SMatthew Barth             requestTarget = _ceiling;
310007de099SMatthew Barth         }
311007de099SMatthew Barth         // Target can not go below the defined floor
312007de099SMatthew Barth         if ((requestTarget < _decDelta) || (requestTarget - _decDelta < _floor))
313007de099SMatthew Barth         {
314007de099SMatthew Barth             requestTarget = _floor;
315007de099SMatthew Barth         }
316007de099SMatthew Barth         else
317007de099SMatthew Barth         {
318007de099SMatthew Barth             requestTarget = requestTarget - _decDelta;
319007de099SMatthew Barth         }
320007de099SMatthew Barth         setTarget(requestTarget);
321007de099SMatthew Barth     }
322007de099SMatthew Barth     // Clear decrease delta when timer expires
323007de099SMatthew Barth     _decDelta = 0;
324007de099SMatthew Barth     // Decrease timer is restarted since its repeating
325007de099SMatthew Barth }
326007de099SMatthew Barth 
327a0dd1350SMatthew Barth void Zone::setPersisted(const std::string& intf, const std::string& prop)
328a0dd1350SMatthew Barth {
329a0dd1350SMatthew Barth     if (std::find_if(_propsPersisted[intf].begin(), _propsPersisted[intf].end(),
330bc89a8a0SMatthew Barth                      [&prop](const auto& p) { return prop == p; }) ==
331a0dd1350SMatthew Barth         _propsPersisted[intf].end())
332a0dd1350SMatthew Barth     {
333a0dd1350SMatthew Barth         _propsPersisted[intf].emplace_back(prop);
334a0dd1350SMatthew Barth     }
335a0dd1350SMatthew Barth }
336a0dd1350SMatthew Barth 
337279183feSMatthew Barth bool Zone::isPersisted(const std::string& intf, const std::string& prop) const
338279183feSMatthew Barth {
339279183feSMatthew Barth     auto it = _propsPersisted.find(intf);
340279183feSMatthew Barth     if (it == _propsPersisted.end())
341279183feSMatthew Barth     {
342279183feSMatthew Barth         return false;
343279183feSMatthew Barth     }
344279183feSMatthew Barth 
345279183feSMatthew Barth     return std::any_of(it->second.begin(), it->second.end(),
346279183feSMatthew Barth                        [&prop](const auto& p) { return prop == p; });
347279183feSMatthew Barth }
348279183feSMatthew Barth 
349ab8e4b82SMatthew Barth void Zone::setPowerOnTarget(const json& jsonObj)
3504f0d3b74SMatthew Barth {
35112e888fbSMatthew Barth     if (!jsonObj.contains("poweron_target"))
3524f0d3b74SMatthew Barth     {
353ab8e4b82SMatthew Barth         auto msg = "Missing required zone's poweron target";
354ab8e4b82SMatthew Barth         log<level::ERR>(msg, entry("JSON=%s", jsonObj.dump().c_str()));
355ab8e4b82SMatthew Barth         throw std::runtime_error(msg);
3564f0d3b74SMatthew Barth     }
357ab8e4b82SMatthew Barth     _poweronTarget = jsonObj["poweron_target"].get<uint64_t>();
3584f0d3b74SMatthew Barth }
3594f0d3b74SMatthew Barth 
360651f03a4SMatthew Barth void Zone::setInterfaces(const json& jsonObj)
361651f03a4SMatthew Barth {
362651f03a4SMatthew Barth     for (const auto& interface : jsonObj["interfaces"])
363651f03a4SMatthew Barth     {
364651f03a4SMatthew Barth         if (!interface.contains("name") || !interface.contains("properties"))
365651f03a4SMatthew Barth         {
366651f03a4SMatthew Barth             log<level::ERR>("Missing required zone interface attributes",
367651f03a4SMatthew Barth                             entry("JSON=%s", interface.dump().c_str()));
368651f03a4SMatthew Barth             throw std::runtime_error(
369651f03a4SMatthew Barth                 "Missing required zone interface attributes");
370651f03a4SMatthew Barth         }
371216229c1SMatthew Barth         auto propFuncs =
372216229c1SMatthew Barth             _intfPropHandlers.find(interface["name"].get<std::string>());
373216229c1SMatthew Barth         if (propFuncs == _intfPropHandlers.end())
374216229c1SMatthew Barth         {
375216229c1SMatthew Barth             // Construct list of available configurable interfaces
376216229c1SMatthew Barth             auto intfs = std::accumulate(
377216229c1SMatthew Barth                 std::next(_intfPropHandlers.begin()), _intfPropHandlers.end(),
378216229c1SMatthew Barth                 _intfPropHandlers.begin()->first, [](auto list, auto intf) {
379216229c1SMatthew Barth                     return std::move(list) + ", " + intf.first;
380216229c1SMatthew Barth                 });
381216229c1SMatthew Barth             log<level::ERR>("Configured interface not available",
382216229c1SMatthew Barth                             entry("JSON=%s", interface.dump().c_str()),
383216229c1SMatthew Barth                             entry("AVAILABLE_INTFS=%s", intfs.c_str()));
384216229c1SMatthew Barth             throw std::runtime_error("Configured interface not available");
385216229c1SMatthew Barth         }
386216229c1SMatthew Barth 
387651f03a4SMatthew Barth         for (const auto& property : interface["properties"])
388651f03a4SMatthew Barth         {
389651f03a4SMatthew Barth             if (!property.contains("name"))
390651f03a4SMatthew Barth             {
391651f03a4SMatthew Barth                 log<level::ERR>(
392651f03a4SMatthew Barth                     "Missing required interface property attributes",
393651f03a4SMatthew Barth                     entry("JSON=%s", property.dump().c_str()));
394651f03a4SMatthew Barth                 throw std::runtime_error(
395651f03a4SMatthew Barth                     "Missing required interface property attributes");
396651f03a4SMatthew Barth             }
397651f03a4SMatthew Barth             // Attribute "persist" is optional, defaults to `false`
398651f03a4SMatthew Barth             auto persist = false;
399651f03a4SMatthew Barth             if (property.contains("persist"))
400651f03a4SMatthew Barth             {
401651f03a4SMatthew Barth                 persist = property["persist"].get<bool>();
402651f03a4SMatthew Barth             }
403651f03a4SMatthew Barth             // Property name from JSON must exactly match supported
404651f03a4SMatthew Barth             // index names to functions in property namespace
405216229c1SMatthew Barth             auto propFunc =
406216229c1SMatthew Barth                 propFuncs->second.find(property["name"].get<std::string>());
407216229c1SMatthew Barth             if (propFunc == propFuncs->second.end())
408651f03a4SMatthew Barth             {
409216229c1SMatthew Barth                 // Construct list of available configurable properties
410651f03a4SMatthew Barth                 auto props = std::accumulate(
411216229c1SMatthew Barth                     std::next(propFuncs->second.begin()),
412216229c1SMatthew Barth                     propFuncs->second.end(), propFuncs->second.begin()->first,
413216229c1SMatthew Barth                     [](auto list, auto prop) {
414651f03a4SMatthew Barth                         return std::move(list) + ", " + prop.first;
415651f03a4SMatthew Barth                     });
416216229c1SMatthew Barth                 log<level::ERR>("Configured property not available",
417651f03a4SMatthew Barth                                 entry("JSON=%s", property.dump().c_str()),
418651f03a4SMatthew Barth                                 entry("AVAILABLE_PROPS=%s", props.c_str()));
419651f03a4SMatthew Barth                 throw std::runtime_error(
420651f03a4SMatthew Barth                     "Configured property function not available");
421651f03a4SMatthew Barth             }
422651f03a4SMatthew Barth 
423bc89a8a0SMatthew Barth             _propInitFunctions.emplace_back(
424bc89a8a0SMatthew Barth                 propFunc->second(property, persist));
425a4483746SMatthew Barth         }
426a4483746SMatthew Barth     }
427a4483746SMatthew Barth }
428a4483746SMatthew Barth 
4299db6dd1dSMatt Spinler json Zone::dump() const
4309db6dd1dSMatt Spinler {
4319db6dd1dSMatt Spinler     json output;
4329db6dd1dSMatt Spinler 
4339db6dd1dSMatt Spinler     output["active"] = _isActive;
4349db6dd1dSMatt Spinler     output["floor"] = _floor;
4359db6dd1dSMatt Spinler     output["target"] = _target;
4369db6dd1dSMatt Spinler     output["increase_delta"] = _incDelta;
4379db6dd1dSMatt Spinler     output["decrease_delta"] = _decDelta;
4389db6dd1dSMatt Spinler     output["power_on_target"] = _poweronTarget;
4399db6dd1dSMatt Spinler     output["default_ceiling"] = _defaultCeiling;
4409db6dd1dSMatt Spinler     output["default_floor"] = _defaultFloor;
4419db6dd1dSMatt Spinler     output["increase_delay"] = _incDelay.count();
4429db6dd1dSMatt Spinler     output["decrease_interval"] = _decInterval.count();
4439db6dd1dSMatt Spinler     output["requested_target_base"] = _requestTargetBase;
4449db6dd1dSMatt Spinler     output["floor_change"] = _floorChange;
4459db6dd1dSMatt Spinler     output["decrease_allowed"] = _decAllowed;
4469db6dd1dSMatt Spinler     output["persisted_props"] = _propsPersisted;
447*40554d8cSMatt Spinler     output["target_holds"] = _targetHolds;
448*40554d8cSMatt Spinler     output["floor_holds"] = _floorHolds;
4499db6dd1dSMatt Spinler 
4509db6dd1dSMatt Spinler     return output;
4519db6dd1dSMatt Spinler }
4529db6dd1dSMatt Spinler 
453651f03a4SMatthew Barth /**
454216229c1SMatthew Barth  * Properties of interfaces supported by the zone configuration that return
455ab8e4b82SMatthew Barth  * a handler function that sets the zone's property value(s) and persist
456ab8e4b82SMatthew Barth  * state.
457651f03a4SMatthew Barth  */
458651f03a4SMatthew Barth namespace zone::property
459651f03a4SMatthew Barth {
460b584d818SMatthew Barth // Get a set property handler function for the configured values of the
461b584d818SMatthew Barth // "Supported" property
462bc89a8a0SMatthew Barth std::function<void(DBusZone&, Zone&)> supported(const json& jsonObj,
463bc89a8a0SMatthew Barth                                                 bool persist)
464651f03a4SMatthew Barth {
465651f03a4SMatthew Barth     std::vector<std::string> values;
466216229c1SMatthew Barth     if (!jsonObj.contains("values"))
467216229c1SMatthew Barth     {
468ab8e4b82SMatthew Barth         log<level::ERR>("No 'values' found for \"Supported\" property, "
469ab8e4b82SMatthew Barth                         "using an empty list",
470216229c1SMatthew Barth                         entry("JSON=%s", jsonObj.dump().c_str()));
471216229c1SMatthew Barth     }
472216229c1SMatthew Barth     else
473216229c1SMatthew Barth     {
474651f03a4SMatthew Barth         for (const auto& value : jsonObj["values"])
475651f03a4SMatthew Barth         {
476216229c1SMatthew Barth             if (!value.contains("value"))
477216229c1SMatthew Barth             {
478216229c1SMatthew Barth                 log<level::ERR>("No 'value' found for \"Supported\" property "
479216229c1SMatthew Barth                                 "entry, skipping",
480216229c1SMatthew Barth                                 entry("JSON=%s", value.dump().c_str()));
481216229c1SMatthew Barth             }
482216229c1SMatthew Barth             else
483216229c1SMatthew Barth             {
484651f03a4SMatthew Barth                 values.emplace_back(value["value"].get<std::string>());
485651f03a4SMatthew Barth             }
486216229c1SMatthew Barth         }
487651f03a4SMatthew Barth     }
488651f03a4SMatthew Barth 
489b584d818SMatthew Barth     return Zone::setProperty<std::vector<std::string>>(
490bc89a8a0SMatthew Barth         DBusZone::thermalModeIntf, DBusZone::supportedProp,
491bc89a8a0SMatthew Barth         &DBusZone::supported, std::move(values), persist);
492216229c1SMatthew Barth }
493216229c1SMatthew Barth 
494ab8e4b82SMatthew Barth // Get a set property handler function for a configured value of the
495ab8e4b82SMatthew Barth // "Current" property
496bc89a8a0SMatthew Barth std::function<void(DBusZone&, Zone&)> current(const json& jsonObj, bool persist)
497651f03a4SMatthew Barth {
498216229c1SMatthew Barth     // Use default value for "Current" property if no "value" entry given
499216229c1SMatthew Barth     if (!jsonObj.contains("value"))
500216229c1SMatthew Barth     {
501b584d818SMatthew Barth         log<level::INFO>("No 'value' found for \"Current\" property, "
502216229c1SMatthew Barth                          "using default",
503216229c1SMatthew Barth                          entry("JSON=%s", jsonObj.dump().c_str()));
504b584d818SMatthew Barth         // Set persist state of property
505bc89a8a0SMatthew Barth         return Zone::setPropertyPersist(DBusZone::thermalModeIntf,
506bc89a8a0SMatthew Barth                                         DBusZone::currentProp, persist);
507216229c1SMatthew Barth     }
508216229c1SMatthew Barth 
509b584d818SMatthew Barth     return Zone::setProperty<std::string>(
510bc89a8a0SMatthew Barth         DBusZone::thermalModeIntf, DBusZone::currentProp, &DBusZone::current,
511b584d818SMatthew Barth         jsonObj["value"].get<std::string>(), persist);
512651f03a4SMatthew Barth }
513b584d818SMatthew Barth 
514651f03a4SMatthew Barth } // namespace zone::property
515651f03a4SMatthew Barth 
5164f0d3b74SMatthew Barth } // namespace phosphor::fan::control::json
517