blob: 8dc60446bca7199f6e610579be4436a56789c7fb [file] [log] [blame]
Matthew Barth4f0d3b72020-08-27 14:32:15 -05001/**
Mike Capps762e8582021-10-07 15:33:23 -04002 * Copyright © 2022 IBM Corporation
Matthew Barth4f0d3b72020-08-27 14:32:15 -05003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "zone.hpp"
17
Matt Spinler40554d82021-10-26 15:23:59 -050018#include "../utils/flight_recorder.hpp"
Matthew Barthbc89a8a2021-05-25 15:42:58 -050019#include "dbus_zone.hpp"
Matthew Barthde90fb42021-03-04 16:34:28 -060020#include "fan.hpp"
Matthew Barth9403a212021-05-17 09:31:50 -050021#include "sdbusplus.hpp"
Matthew Barth216229c2020-09-24 13:47:33 -050022
Matthew Barth4f0d3b72020-08-27 14:32:15 -050023#include <nlohmann/json.hpp>
24#include <phosphor-logging/log.hpp>
Matthew Barth603ef162021-03-24 15:34:53 -050025#include <sdeventplus/event.hpp>
Matthew Barth4f0d3b72020-08-27 14:32:15 -050026
Matthew Bartha0dd1352021-03-09 11:10:49 -060027#include <algorithm>
Matthew Barth007de092021-03-25 13:56:04 -050028#include <chrono>
Matthew Barth651f03a2020-08-27 16:15:11 -050029#include <iterator>
30#include <map>
Matthew Barthbc89a8a2021-05-25 15:42:58 -050031#include <memory>
Matthew Barth651f03a2020-08-27 16:15:11 -050032#include <numeric>
Matthew Barth651f03a2020-08-27 16:15:11 -050033#include <utility>
Matthew Barth216229c2020-09-24 13:47:33 -050034#include <vector>
Matthew Barth651f03a2020-08-27 16:15:11 -050035
Matthew Barth4f0d3b72020-08-27 14:32:15 -050036namespace phosphor::fan::control::json
37{
38
39using json = nlohmann::json;
40using namespace phosphor::logging;
41
Matthew Barthbc89a8a2021-05-25 15:42:58 -050042const std::map<
43 std::string,
44 std::map<std::string, std::function<std::function<void(DBusZone&, Zone&)>(
45 const json&, bool)>>>
46 Zone::_intfPropHandlers = {
47 {DBusZone::thermalModeIntf,
48 {{DBusZone::supportedProp, zone::property::supported},
49 {DBusZone::currentProp, zone::property::current}}}};
Matthew Barth651f03a2020-08-27 16:15:11 -050050
Matthew Barth9403a212021-05-17 09:31:50 -050051Zone::Zone(const json& jsonObj, const sdeventplus::Event& event, Manager* mgr) :
Matthew Barthab8e4b82021-05-27 13:25:46 -050052 ConfigBase(jsonObj), _dbusZone{}, _manager(mgr), _defaultFloor(0),
Matthew Barth2504c772021-05-27 14:02:31 -050053 _incDelay(0), _decInterval(0), _floor(0), _target(0), _incDelta(0),
54 _decDelta(0), _requestTargetBase(0), _isActive(true),
Matthew Barthab8e4b82021-05-27 13:25:46 -050055 _incTimer(event, std::bind(&Zone::incTimerExpired, this)),
Matthew Barth007de092021-03-25 13:56:04 -050056 _decTimer(event, std::bind(&Zone::decTimerExpired, this))
Matthew Barth4f0d3b72020-08-27 14:32:15 -050057{
Matthew Barthe47c9582021-03-09 14:24:02 -060058 // Increase delay is optional, defaults to 0
Matthew Barth4f0d3b72020-08-27 14:32:15 -050059 if (jsonObj.contains("increase_delay"))
60 {
Matthew Barth007de092021-03-25 13:56:04 -050061 _incDelay =
62 std::chrono::seconds(jsonObj["increase_delay"].get<uint64_t>());
Matthew Barth4f0d3b72020-08-27 14:32:15 -050063 }
Matthew Barthab8e4b82021-05-27 13:25:46 -050064
65 // Poweron target is required
66 setPowerOnTarget(jsonObj);
67
68 // Default ceiling is optional, defaults to poweron target
69 _defaultCeiling = _poweronTarget;
70 if (jsonObj.contains("default_ceiling"))
71 {
72 _defaultCeiling = jsonObj["default_ceiling"].get<uint64_t>();
73 }
74 // Start with the current ceiling set as the default ceiling
75 _ceiling = _defaultCeiling;
76
77 // Default floor is optional, defaults to 0
78 if (jsonObj.contains("default_floor"))
79 {
80 _defaultFloor = jsonObj["default_floor"].get<uint64_t>();
Matthew Barthdc3152d2022-03-16 14:43:13 -050081 if (_defaultFloor > _ceiling)
82 {
83 log<level::ERR>(
Patrick Williamsfbf47032023-07-17 12:27:34 -050084 std::format("Configured default_floor({}) above ceiling({}), "
Matthew Barthdc3152d2022-03-16 14:43:13 -050085 "setting default floor to ceiling",
86 _defaultFloor, _ceiling)
87 .c_str());
88 _defaultFloor = _ceiling;
89 }
Matthew Barthab8e4b82021-05-27 13:25:46 -050090 // Start with the current floor set as the default
91 _floor = _defaultFloor;
92 }
93
Matthew Barth2504c772021-05-27 14:02:31 -050094 // Decrease interval is optional, defaults to 0
95 // A decrease interval of 0sec disables the decrease timer
96 if (jsonObj.contains("decrease_interval"))
97 {
98 _decInterval =
99 std::chrono::seconds(jsonObj["decrease_interval"].get<uint64_t>());
100 }
Matthew Barthab8e4b82021-05-27 13:25:46 -0500101
Matthew Barth651f03a2020-08-27 16:15:11 -0500102 // Setting properties on interfaces to be served are optional
103 if (jsonObj.contains("interfaces"))
104 {
105 setInterfaces(jsonObj);
106 }
Matthew Barth14303a42021-05-21 13:04:08 -0500107}
Matthew Barth007de092021-03-25 13:56:04 -0500108
Matthew Barth14303a42021-05-21 13:04:08 -0500109void Zone::enable()
110{
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500111 // Create thermal control dbus object
112 _dbusZone = std::make_unique<DBusZone>(*this);
Matthew Bartha4483742021-03-25 14:09:01 -0500113
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500114 // Init all configured dbus interfaces' property states
115 for (const auto& func : _propInitFunctions)
116 {
117 // Only call non-null init property functions
118 if (func)
119 {
120 func(*_dbusZone, *this);
121 }
122 }
123
124 // TODO - Restore any persisted properties in init function
125 // Restore thermal control current mode state, if exists
126 _dbusZone->restoreCurrentMode();
127
128 // Emit object added for this zone's associated dbus object
129 _dbusZone->emit_object_added();
Matthew Bartha4483742021-03-25 14:09:01 -0500130
Matthew Barth2504c772021-05-27 14:02:31 -0500131 // A decrease interval of 0sec disables the decrease timer
132 if (_decInterval != std::chrono::seconds::zero())
133 {
134 // Start timer for fan target decreases
135 _decTimer.restart(_decInterval);
136 }
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500137}
138
Matthew Barthde90fb42021-03-04 16:34:28 -0600139void Zone::addFan(std::unique_ptr<Fan> fan)
140{
141 _fans.emplace_back(std::move(fan));
142}
143
Matthew Barth8ba715e2021-03-05 09:00:05 -0600144void Zone::setTarget(uint64_t target)
145{
Matt Spinler5a2b5012021-07-22 14:13:19 -0600146 if (_isActive)
Matthew Barth8ba715e2021-03-05 09:00:05 -0600147 {
Matt Spinlera917e692022-04-13 14:50:08 -0500148 if (_target != target)
149 {
150 FlightRecorder::instance().log(
151 "zone-set-target" + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500152 std::format("Set target {} (from {})", target, _target));
Matt Spinlera917e692022-04-13 14:50:08 -0500153 }
Matthew Barth8ba715e2021-03-05 09:00:05 -0600154 _target = target;
155 for (auto& fan : _fans)
156 {
157 fan->setTarget(_target);
158 }
159 }
160}
161
Mike Capps762e8582021-10-07 15:33:23 -0400162void Zone::lockFanTarget(const std::string& fname, uint64_t target)
163{
Patrick Williamsdfddd642024-08-16 15:21:51 -0400164 auto fanItr =
165 std::find_if(_fans.begin(), _fans.end(), [&fname](const auto& fan) {
166 return fan->getName() == fname;
167 });
Mike Capps762e8582021-10-07 15:33:23 -0400168
169 if (_fans.end() != fanItr)
170 {
171 (*fanItr)->lockTarget(target);
172 }
173 else
174 {
175 log<level::DEBUG>(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500176 std::format("Configured fan {} not found in zone {} to lock target",
Mike Capps762e8582021-10-07 15:33:23 -0400177 fname, getName())
178 .c_str());
179 }
180}
181
182void Zone::unlockFanTarget(const std::string& fname, uint64_t target)
183{
Patrick Williamsdfddd642024-08-16 15:21:51 -0400184 auto fanItr =
185 std::find_if(_fans.begin(), _fans.end(), [&fname](const auto& fan) {
186 return fan->getName() == fname;
187 });
Mike Capps762e8582021-10-07 15:33:23 -0400188
189 if (_fans.end() != fanItr)
190 {
191 (*fanItr)->unlockTarget(target);
192
193 // attempt to resume Zone target on fan
194 (*fanItr)->setTarget(getTarget());
195 }
196 else
197 {
198 log<level::DEBUG>(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500199 std::format(
Mike Capps762e8582021-10-07 15:33:23 -0400200 "Configured fan {} not found in zone {} to unlock target",
201 fname, getName())
202 .c_str());
203 }
204}
205
Matthew Barth53680112021-09-23 11:20:41 -0500206void Zone::setTargetHold(const std::string& ident, uint64_t target, bool hold)
207{
Matt Spinler98671612021-12-09 14:27:15 -0600208 using namespace std::string_literals;
209
Matthew Barth53680112021-09-23 11:20:41 -0500210 if (!hold)
211 {
Matt Spinler98671612021-12-09 14:27:15 -0600212 size_t removed = _targetHolds.erase(ident);
213 if (removed)
214 {
215 FlightRecorder::instance().log(
216 "zone-target"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500217 std::format("{} is removing target hold", ident));
Matt Spinler98671612021-12-09 14:27:15 -0600218 }
Matthew Barth53680112021-09-23 11:20:41 -0500219 }
220 else
221 {
Matt Spinler98671612021-12-09 14:27:15 -0600222 if (!((_targetHolds.find(ident) != _targetHolds.end()) &&
223 (_targetHolds[ident] == target)))
224 {
225 FlightRecorder::instance().log(
226 "zone-target"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500227 std::format("{} is setting target hold to {}", ident, target));
Matt Spinler98671612021-12-09 14:27:15 -0600228 }
Matt Spinler40554d82021-10-26 15:23:59 -0500229 _targetHolds[ident] = target;
Matthew Barth53680112021-09-23 11:20:41 -0500230 _isActive = false;
231 }
232
Matt Spinler40554d82021-10-26 15:23:59 -0500233 auto itHoldMax = std::max_element(_targetHolds.begin(), _targetHolds.end(),
Matthew Barth53680112021-09-23 11:20:41 -0500234 [](const auto& aHold, const auto& bHold) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400235 return aHold.second < bHold.second;
236 });
Matt Spinler40554d82021-10-26 15:23:59 -0500237 if (itHoldMax == _targetHolds.end())
Matthew Barth53680112021-09-23 11:20:41 -0500238 {
239 _isActive = true;
240 }
241 else
242 {
Matt Spinler98671612021-12-09 14:27:15 -0600243 if (_target != itHoldMax->second)
244 {
245 FlightRecorder::instance().log(
246 "zone-target"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500247 std::format("Settings fans to target hold of {}",
Matt Spinler98671612021-12-09 14:27:15 -0600248 itHoldMax->second));
249 }
250
Matthew Barth53680112021-09-23 11:20:41 -0500251 _target = itHoldMax->second;
252 for (auto& fan : _fans)
253 {
254 fan->setTarget(_target);
255 }
256 }
257}
258
Matt Spinler40554d82021-10-26 15:23:59 -0500259void Zone::setFloorHold(const std::string& ident, uint64_t target, bool hold)
260{
261 using namespace std::string_literals;
262
Matthew Barthdc3152d2022-03-16 14:43:13 -0500263 if (target > _ceiling)
264 {
265 target = _ceiling;
266 }
267
Matt Spinler40554d82021-10-26 15:23:59 -0500268 if (!hold)
269 {
270 size_t removed = _floorHolds.erase(ident);
271 if (removed)
272 {
273 FlightRecorder::instance().log(
274 "zone-floor"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500275 std::format("{} is removing floor hold", ident));
Matt Spinler40554d82021-10-26 15:23:59 -0500276 }
277 }
278 else
279 {
280 if (!((_floorHolds.find(ident) != _floorHolds.end()) &&
281 (_floorHolds[ident] == target)))
282 {
283 FlightRecorder::instance().log(
284 "zone-floor"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500285 std::format("{} is setting floor hold to {}", ident, target));
Matt Spinler40554d82021-10-26 15:23:59 -0500286 }
287 _floorHolds[ident] = target;
288 }
289
290 if (!std::all_of(_floorChange.begin(), _floorChange.end(),
291 [](const auto& entry) { return entry.second; }))
292 {
293 return;
294 }
295
296 auto itHoldMax = std::max_element(_floorHolds.begin(), _floorHolds.end(),
297 [](const auto& aHold, const auto& bHold) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400298 return aHold.second < bHold.second;
299 });
Matt Spinler40554d82021-10-26 15:23:59 -0500300 if (itHoldMax == _floorHolds.end())
301 {
302 if (_floor != _defaultFloor)
303 {
304 FlightRecorder::instance().log(
305 "zone-floor"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500306 std::format("No set floor exists, using default floor",
Matt Spinler40554d82021-10-26 15:23:59 -0500307 _defaultFloor));
308 }
309 _floor = _defaultFloor;
310 }
311 else
312 {
313 if (_floor != itHoldMax->second)
314 {
315 FlightRecorder::instance().log(
316 "zone-floor"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500317 std::format("Setting new floor to {}", itHoldMax->second));
Matt Spinler40554d82021-10-26 15:23:59 -0500318 }
319 _floor = itHoldMax->second;
320 }
321
322 // Floor above target, update target to floor
323 if (_target < _floor)
324 {
325 requestIncrease(_floor - _target);
326 }
327}
328
Matthew Barth12cb1252021-03-08 16:47:30 -0600329void Zone::setFloor(uint64_t target)
330{
331 // Check all entries are set to allow floor to be set
Matthew Barth8ba715e2021-03-05 09:00:05 -0600332 auto pred = [](const auto& entry) { return entry.second; };
Matthew Barth12cb1252021-03-08 16:47:30 -0600333 if (std::all_of(_floorChange.begin(), _floorChange.end(), pred))
334 {
Matthew Barthdc3152d2022-03-16 14:43:13 -0500335 _floor = (target > _ceiling) ? _ceiling : target;
Matthew Barth12cb1252021-03-08 16:47:30 -0600336 // Floor above target, update target to floor
337 if (_target < _floor)
338 {
339 requestIncrease(_floor - _target);
340 }
341 }
342}
343
344void Zone::requestIncrease(uint64_t targetDelta)
345{
Matthew Barth2b3253e2021-03-09 14:51:16 -0600346 // Only increase when delta is higher than the current increase delta for
347 // the zone and currently under ceiling
348 if (targetDelta > _incDelta && _target < _ceiling)
349 {
350 auto requestTarget = getRequestTargetBase();
351 requestTarget = (targetDelta - _incDelta) + requestTarget;
352 _incDelta = targetDelta;
353 // Target can not go above a current ceiling
354 if (requestTarget > _ceiling)
355 {
356 requestTarget = _ceiling;
357 }
Matthew Barth8ba715e2021-03-05 09:00:05 -0600358 setTarget(requestTarget);
Matthew Barth007de092021-03-25 13:56:04 -0500359 // Restart timer countdown for fan target increase
360 _incTimer.restartOnce(_incDelay);
Matthew Barth2b3253e2021-03-09 14:51:16 -0600361 }
Matthew Barth12cb1252021-03-08 16:47:30 -0600362}
363
Matthew Barth007de092021-03-25 13:56:04 -0500364void Zone::incTimerExpired()
365{
366 // Clear increase delta when timer expires allowing additional target
367 // increase requests or target decreases to occur
368 _incDelta = 0;
369}
370
Matthew Barth45c44ea2021-03-03 13:16:14 -0600371void Zone::requestDecrease(uint64_t targetDelta)
372{
373 // Only decrease the lowest target delta requested
374 if (_decDelta == 0 || targetDelta < _decDelta)
375 {
376 _decDelta = targetDelta;
377 }
378}
379
Matthew Barth007de092021-03-25 13:56:04 -0500380void Zone::decTimerExpired()
381{
382 // Check all entries are set to allow a decrease
Patrick Williams61b73292023-05-10 07:50:12 -0500383 auto pred = [](const auto& entry) { return entry.second; };
Matthew Barth007de092021-03-25 13:56:04 -0500384 auto decAllowed = std::all_of(_decAllowed.begin(), _decAllowed.end(), pred);
385
386 // Only decrease targets when allowed, a requested decrease target delta
387 // exists, where no requested increases exist and the increase timer is not
388 // running (i.e. not in the middle of increasing)
389 if (decAllowed && _decDelta != 0 && _incDelta == 0 &&
390 !_incTimer.isEnabled())
391 {
392 auto requestTarget = getRequestTargetBase();
393 // Request target should not start above ceiling
394 if (requestTarget > _ceiling)
395 {
396 requestTarget = _ceiling;
397 }
398 // Target can not go below the defined floor
399 if ((requestTarget < _decDelta) || (requestTarget - _decDelta < _floor))
400 {
401 requestTarget = _floor;
402 }
403 else
404 {
405 requestTarget = requestTarget - _decDelta;
406 }
407 setTarget(requestTarget);
408 }
409 // Clear decrease delta when timer expires
410 _decDelta = 0;
411 // Decrease timer is restarted since its repeating
412}
413
Matthew Bartha0dd1352021-03-09 11:10:49 -0600414void Zone::setPersisted(const std::string& intf, const std::string& prop)
415{
416 if (std::find_if(_propsPersisted[intf].begin(), _propsPersisted[intf].end(),
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500417 [&prop](const auto& p) { return prop == p; }) ==
Matthew Bartha0dd1352021-03-09 11:10:49 -0600418 _propsPersisted[intf].end())
419 {
420 _propsPersisted[intf].emplace_back(prop);
421 }
422}
423
Matthew Barth279183f2021-05-25 10:19:43 -0500424bool Zone::isPersisted(const std::string& intf, const std::string& prop) const
425{
426 auto it = _propsPersisted.find(intf);
427 if (it == _propsPersisted.end())
428 {
429 return false;
430 }
431
432 return std::any_of(it->second.begin(), it->second.end(),
433 [&prop](const auto& p) { return prop == p; });
434}
435
Matthew Barthab8e4b82021-05-27 13:25:46 -0500436void Zone::setPowerOnTarget(const json& jsonObj)
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500437{
Matthew Barth12e888f2021-08-20 11:41:32 -0500438 if (!jsonObj.contains("poweron_target"))
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500439 {
Matthew Barthab8e4b82021-05-27 13:25:46 -0500440 auto msg = "Missing required zone's poweron target";
441 log<level::ERR>(msg, entry("JSON=%s", jsonObj.dump().c_str()));
442 throw std::runtime_error(msg);
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500443 }
Matthew Barth12e888f2021-08-20 11:41:32 -0500444 _poweronTarget = jsonObj["poweron_target"].get<uint64_t>();
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500445}
446
Matthew Barth651f03a2020-08-27 16:15:11 -0500447void Zone::setInterfaces(const json& jsonObj)
448{
449 for (const auto& interface : jsonObj["interfaces"])
450 {
451 if (!interface.contains("name") || !interface.contains("properties"))
452 {
453 log<level::ERR>("Missing required zone interface attributes",
454 entry("JSON=%s", interface.dump().c_str()));
455 throw std::runtime_error(
456 "Missing required zone interface attributes");
457 }
Matthew Barth216229c2020-09-24 13:47:33 -0500458 auto propFuncs =
459 _intfPropHandlers.find(interface["name"].get<std::string>());
460 if (propFuncs == _intfPropHandlers.end())
461 {
462 // Construct list of available configurable interfaces
Patrick Williams5e15c3b2023-10-20 11:18:11 -0500463 auto intfs = std::accumulate(
464 std::next(_intfPropHandlers.begin()), _intfPropHandlers.end(),
465 _intfPropHandlers.begin()->first, [](auto list, auto intf) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400466 return std::move(list) + ", " + intf.first;
467 });
Matthew Barth216229c2020-09-24 13:47:33 -0500468 log<level::ERR>("Configured interface not available",
469 entry("JSON=%s", interface.dump().c_str()),
470 entry("AVAILABLE_INTFS=%s", intfs.c_str()));
471 throw std::runtime_error("Configured interface not available");
472 }
473
Matthew Barth651f03a2020-08-27 16:15:11 -0500474 for (const auto& property : interface["properties"])
475 {
476 if (!property.contains("name"))
477 {
478 log<level::ERR>(
479 "Missing required interface property attributes",
480 entry("JSON=%s", property.dump().c_str()));
481 throw std::runtime_error(
482 "Missing required interface property attributes");
483 }
484 // Attribute "persist" is optional, defaults to `false`
485 auto persist = false;
486 if (property.contains("persist"))
487 {
488 persist = property["persist"].get<bool>();
489 }
490 // Property name from JSON must exactly match supported
491 // index names to functions in property namespace
Matthew Barth216229c2020-09-24 13:47:33 -0500492 auto propFunc =
493 propFuncs->second.find(property["name"].get<std::string>());
494 if (propFunc == propFuncs->second.end())
Matthew Barth651f03a2020-08-27 16:15:11 -0500495 {
Matthew Barth216229c2020-09-24 13:47:33 -0500496 // Construct list of available configurable properties
Matthew Barth651f03a2020-08-27 16:15:11 -0500497 auto props = std::accumulate(
Matthew Barth216229c2020-09-24 13:47:33 -0500498 std::next(propFuncs->second.begin()),
499 propFuncs->second.end(), propFuncs->second.begin()->first,
500 [](auto list, auto prop) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400501 return std::move(list) + ", " + prop.first;
502 });
Matthew Barth216229c2020-09-24 13:47:33 -0500503 log<level::ERR>("Configured property not available",
Matthew Barth651f03a2020-08-27 16:15:11 -0500504 entry("JSON=%s", property.dump().c_str()),
505 entry("AVAILABLE_PROPS=%s", props.c_str()));
506 throw std::runtime_error(
507 "Configured property function not available");
508 }
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500509
510 _propInitFunctions.emplace_back(
511 propFunc->second(property, persist));
Matthew Barth651f03a2020-08-27 16:15:11 -0500512 }
Matthew Barth651f03a2020-08-27 16:15:11 -0500513 }
514}
515
Matt Spinler9db6dd12021-10-29 16:10:08 -0500516json Zone::dump() const
517{
518 json output;
519
520 output["active"] = _isActive;
521 output["floor"] = _floor;
Matthew Barth077448a2021-12-02 20:38:15 -0600522 output["ceiling"] = _ceiling;
Matt Spinler9db6dd12021-10-29 16:10:08 -0500523 output["target"] = _target;
524 output["increase_delta"] = _incDelta;
525 output["decrease_delta"] = _decDelta;
526 output["power_on_target"] = _poweronTarget;
527 output["default_ceiling"] = _defaultCeiling;
528 output["default_floor"] = _defaultFloor;
529 output["increase_delay"] = _incDelay.count();
530 output["decrease_interval"] = _decInterval.count();
531 output["requested_target_base"] = _requestTargetBase;
532 output["floor_change"] = _floorChange;
533 output["decrease_allowed"] = _decAllowed;
534 output["persisted_props"] = _propsPersisted;
Matt Spinler40554d82021-10-26 15:23:59 -0500535 output["target_holds"] = _targetHolds;
536 output["floor_holds"] = _floorHolds;
Matt Spinler9db6dd12021-10-29 16:10:08 -0500537
Matt Spinler2541f992022-08-16 16:00:04 -0500538 std::map<std::string, std::vector<uint64_t>> lockedTargets;
539 for (const auto& fan : _fans)
540 {
541 const auto& locks = fan->getLockedTargets();
542 if (!locks.empty())
543 {
544 lockedTargets[fan->getName()] = locks;
545 }
546 }
547 output["target_locks"] = lockedTargets;
548
Matt Spinler9db6dd12021-10-29 16:10:08 -0500549 return output;
550}
551
Matthew Barth651f03a2020-08-27 16:15:11 -0500552/**
Matthew Barth216229c2020-09-24 13:47:33 -0500553 * Properties of interfaces supported by the zone configuration that return
Matthew Barthab8e4b82021-05-27 13:25:46 -0500554 * a handler function that sets the zone's property value(s) and persist
555 * state.
Matthew Barth651f03a2020-08-27 16:15:11 -0500556 */
557namespace zone::property
558{
Matthew Barthb584d812021-03-11 15:55:04 -0600559// Get a set property handler function for the configured values of the
560// "Supported" property
Patrick Williamsdfddd642024-08-16 15:21:51 -0400561std::function<void(DBusZone&, Zone&)>
562 supported(const json& jsonObj, bool persist)
Matthew Barth651f03a2020-08-27 16:15:11 -0500563{
564 std::vector<std::string> values;
Matthew Barth216229c2020-09-24 13:47:33 -0500565 if (!jsonObj.contains("values"))
Matthew Barth651f03a2020-08-27 16:15:11 -0500566 {
Matthew Barthab8e4b82021-05-27 13:25:46 -0500567 log<level::ERR>("No 'values' found for \"Supported\" property, "
568 "using an empty list",
569 entry("JSON=%s", jsonObj.dump().c_str()));
Matthew Barth216229c2020-09-24 13:47:33 -0500570 }
571 else
572 {
573 for (const auto& value : jsonObj["values"])
574 {
575 if (!value.contains("value"))
576 {
577 log<level::ERR>("No 'value' found for \"Supported\" property "
578 "entry, skipping",
579 entry("JSON=%s", value.dump().c_str()));
580 }
581 else
582 {
583 values.emplace_back(value["value"].get<std::string>());
584 }
585 }
Matthew Barth651f03a2020-08-27 16:15:11 -0500586 }
587
Matthew Barthb584d812021-03-11 15:55:04 -0600588 return Zone::setProperty<std::vector<std::string>>(
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500589 DBusZone::thermalModeIntf, DBusZone::supportedProp,
590 &DBusZone::supported, std::move(values), persist);
Matthew Barth651f03a2020-08-27 16:15:11 -0500591}
592
Matthew Barthab8e4b82021-05-27 13:25:46 -0500593// Get a set property handler function for a configured value of the
594// "Current" property
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500595std::function<void(DBusZone&, Zone&)> current(const json& jsonObj, bool persist)
Matthew Barth651f03a2020-08-27 16:15:11 -0500596{
Matthew Barth216229c2020-09-24 13:47:33 -0500597 // Use default value for "Current" property if no "value" entry given
598 if (!jsonObj.contains("value"))
599 {
Matthew Barthb584d812021-03-11 15:55:04 -0600600 log<level::INFO>("No 'value' found for \"Current\" property, "
601 "using default",
602 entry("JSON=%s", jsonObj.dump().c_str()));
603 // Set persist state of property
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500604 return Zone::setPropertyPersist(DBusZone::thermalModeIntf,
605 DBusZone::currentProp, persist);
Matthew Barth216229c2020-09-24 13:47:33 -0500606 }
607
Matthew Barthb584d812021-03-11 15:55:04 -0600608 return Zone::setProperty<std::string>(
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500609 DBusZone::thermalModeIntf, DBusZone::currentProp, &DBusZone::current,
Matthew Barthb584d812021-03-11 15:55:04 -0600610 jsonObj["value"].get<std::string>(), persist);
Matthew Barth651f03a2020-08-27 16:15:11 -0500611}
Matthew Barthb584d812021-03-11 15:55:04 -0600612
Matthew Barth651f03a2020-08-27 16:15:11 -0500613} // namespace zone::property
614
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500615} // namespace phosphor::fan::control::json