blob: 170b53d799b6a8668f76f2780387846dc2afaf21 [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>
Anwaar Hadi64b5ac22025-04-04 23:54:53 +000024#include <phosphor-logging/lg2.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>
Anwaar Hadi64b5ac22025-04-04 23:54:53 +000029#include <format>
Matthew Barth651f03a2020-08-27 16:15:11 -050030#include <iterator>
31#include <map>
Matthew Barthbc89a8a2021-05-25 15:42:58 -050032#include <memory>
Matthew Barth651f03a2020-08-27 16:15:11 -050033#include <numeric>
Matthew Barth651f03a2020-08-27 16:15:11 -050034#include <utility>
Matthew Barth216229c2020-09-24 13:47:33 -050035#include <vector>
Matthew Barth651f03a2020-08-27 16:15:11 -050036
Matthew Barth4f0d3b72020-08-27 14:32:15 -050037namespace phosphor::fan::control::json
38{
39
40using json = nlohmann::json;
Matthew Barth4f0d3b72020-08-27 14:32:15 -050041
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 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +000083 lg2::error(
84 "Configured default_floor({DEFAULT_FLOOR}) above ceiling({CEILING}), "
85 "setting default floor to ceiling",
86 "DEFAULT_FLOOR", _defaultFloor, "CEILING", _ceiling);
Matthew Barthdc3152d2022-03-16 14:43:13 -050087 _defaultFloor = _ceiling;
88 }
Matthew Barthab8e4b82021-05-27 13:25:46 -050089 // Start with the current floor set as the default
90 _floor = _defaultFloor;
91 }
92
Matthew Barth2504c772021-05-27 14:02:31 -050093 // Decrease interval is optional, defaults to 0
94 // A decrease interval of 0sec disables the decrease timer
95 if (jsonObj.contains("decrease_interval"))
96 {
97 _decInterval =
98 std::chrono::seconds(jsonObj["decrease_interval"].get<uint64_t>());
99 }
Matthew Barthab8e4b82021-05-27 13:25:46 -0500100
Matthew Barth651f03a2020-08-27 16:15:11 -0500101 // Setting properties on interfaces to be served are optional
102 if (jsonObj.contains("interfaces"))
103 {
104 setInterfaces(jsonObj);
105 }
Matthew Barth14303a42021-05-21 13:04:08 -0500106}
Matthew Barth007de092021-03-25 13:56:04 -0500107
Matthew Barth14303a42021-05-21 13:04:08 -0500108void Zone::enable()
109{
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500110 // Create thermal control dbus object
111 _dbusZone = std::make_unique<DBusZone>(*this);
Matthew Bartha4483742021-03-25 14:09:01 -0500112
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500113 // Init all configured dbus interfaces' property states
114 for (const auto& func : _propInitFunctions)
115 {
116 // Only call non-null init property functions
117 if (func)
118 {
119 func(*_dbusZone, *this);
120 }
121 }
122
123 // TODO - Restore any persisted properties in init function
124 // Restore thermal control current mode state, if exists
125 _dbusZone->restoreCurrentMode();
126
127 // Emit object added for this zone's associated dbus object
128 _dbusZone->emit_object_added();
Matthew Bartha4483742021-03-25 14:09:01 -0500129
Matthew Barth2504c772021-05-27 14:02:31 -0500130 // A decrease interval of 0sec disables the decrease timer
131 if (_decInterval != std::chrono::seconds::zero())
132 {
133 // Start timer for fan target decreases
134 _decTimer.restart(_decInterval);
135 }
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500136}
137
Matthew Barthde90fb42021-03-04 16:34:28 -0600138void Zone::addFan(std::unique_ptr<Fan> fan)
139{
140 _fans.emplace_back(std::move(fan));
141}
142
Matthew Barth8ba715e2021-03-05 09:00:05 -0600143void Zone::setTarget(uint64_t target)
144{
Matt Spinler5a2b5012021-07-22 14:13:19 -0600145 if (_isActive)
Matthew Barth8ba715e2021-03-05 09:00:05 -0600146 {
Matt Spinlera917e692022-04-13 14:50:08 -0500147 if (_target != target)
148 {
149 FlightRecorder::instance().log(
150 "zone-set-target" + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500151 std::format("Set target {} (from {})", target, _target));
Matt Spinlera917e692022-04-13 14:50:08 -0500152 }
Matthew Barth8ba715e2021-03-05 09:00:05 -0600153 _target = target;
154 for (auto& fan : _fans)
155 {
156 fan->setTarget(_target);
157 }
158 }
159}
160
Mike Capps762e8582021-10-07 15:33:23 -0400161void Zone::lockFanTarget(const std::string& fname, uint64_t target)
162{
Patrick Williamsdfddd642024-08-16 15:21:51 -0400163 auto fanItr =
164 std::find_if(_fans.begin(), _fans.end(), [&fname](const auto& fan) {
165 return fan->getName() == fname;
166 });
Mike Capps762e8582021-10-07 15:33:23 -0400167
168 if (_fans.end() != fanItr)
169 {
170 (*fanItr)->lockTarget(target);
171 }
172 else
173 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000174 lg2::debug(
175 "Configured fan {FAN} not found in zone {ZONE_NAME} to lock target",
176 "FAN", fname, "ZONE_NAME", getName());
Mike Capps762e8582021-10-07 15:33:23 -0400177 }
178}
179
180void Zone::unlockFanTarget(const std::string& fname, uint64_t target)
181{
Patrick Williamsdfddd642024-08-16 15:21:51 -0400182 auto fanItr =
183 std::find_if(_fans.begin(), _fans.end(), [&fname](const auto& fan) {
184 return fan->getName() == fname;
185 });
Mike Capps762e8582021-10-07 15:33:23 -0400186
187 if (_fans.end() != fanItr)
188 {
189 (*fanItr)->unlockTarget(target);
190
191 // attempt to resume Zone target on fan
192 (*fanItr)->setTarget(getTarget());
193 }
194 else
195 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000196 lg2::debug(
197 "Configured fan {FAN} not found in zone {ZONE_NAME} to unlock target",
198 "FAN", fname, "ZONE_NAME", getName());
Mike Capps762e8582021-10-07 15:33:23 -0400199 }
200}
201
Matthew Barth53680112021-09-23 11:20:41 -0500202void Zone::setTargetHold(const std::string& ident, uint64_t target, bool hold)
203{
Matt Spinler98671612021-12-09 14:27:15 -0600204 using namespace std::string_literals;
205
Matthew Barth53680112021-09-23 11:20:41 -0500206 if (!hold)
207 {
Matt Spinler98671612021-12-09 14:27:15 -0600208 size_t removed = _targetHolds.erase(ident);
209 if (removed)
210 {
211 FlightRecorder::instance().log(
212 "zone-target"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500213 std::format("{} is removing target hold", ident));
Matt Spinler98671612021-12-09 14:27:15 -0600214 }
Matthew Barth53680112021-09-23 11:20:41 -0500215 }
216 else
217 {
Matt Spinler98671612021-12-09 14:27:15 -0600218 if (!((_targetHolds.find(ident) != _targetHolds.end()) &&
219 (_targetHolds[ident] == target)))
220 {
221 FlightRecorder::instance().log(
222 "zone-target"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500223 std::format("{} is setting target hold to {}", ident, target));
Matt Spinler98671612021-12-09 14:27:15 -0600224 }
Matt Spinler40554d82021-10-26 15:23:59 -0500225 _targetHolds[ident] = target;
Matthew Barth53680112021-09-23 11:20:41 -0500226 _isActive = false;
227 }
228
Matt Spinler40554d82021-10-26 15:23:59 -0500229 auto itHoldMax = std::max_element(_targetHolds.begin(), _targetHolds.end(),
Matthew Barth53680112021-09-23 11:20:41 -0500230 [](const auto& aHold, const auto& bHold) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400231 return aHold.second < bHold.second;
232 });
Matt Spinler40554d82021-10-26 15:23:59 -0500233 if (itHoldMax == _targetHolds.end())
Matthew Barth53680112021-09-23 11:20:41 -0500234 {
235 _isActive = true;
236 }
237 else
238 {
Matt Spinler98671612021-12-09 14:27:15 -0600239 if (_target != itHoldMax->second)
240 {
241 FlightRecorder::instance().log(
242 "zone-target"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500243 std::format("Settings fans to target hold of {}",
Matt Spinler98671612021-12-09 14:27:15 -0600244 itHoldMax->second));
245 }
246
Matthew Barth53680112021-09-23 11:20:41 -0500247 _target = itHoldMax->second;
248 for (auto& fan : _fans)
249 {
250 fan->setTarget(_target);
251 }
252 }
253}
254
Matt Spinler40554d82021-10-26 15:23:59 -0500255void Zone::setFloorHold(const std::string& ident, uint64_t target, bool hold)
256{
257 using namespace std::string_literals;
258
Matthew Barthdc3152d2022-03-16 14:43:13 -0500259 if (target > _ceiling)
260 {
261 target = _ceiling;
262 }
263
Matt Spinler40554d82021-10-26 15:23:59 -0500264 if (!hold)
265 {
266 size_t removed = _floorHolds.erase(ident);
267 if (removed)
268 {
269 FlightRecorder::instance().log(
270 "zone-floor"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500271 std::format("{} is removing floor hold", ident));
Matt Spinler40554d82021-10-26 15:23:59 -0500272 }
273 }
274 else
275 {
276 if (!((_floorHolds.find(ident) != _floorHolds.end()) &&
277 (_floorHolds[ident] == target)))
278 {
279 FlightRecorder::instance().log(
280 "zone-floor"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500281 std::format("{} is setting floor hold to {}", ident, target));
Matt Spinler40554d82021-10-26 15:23:59 -0500282 }
283 _floorHolds[ident] = target;
284 }
285
286 if (!std::all_of(_floorChange.begin(), _floorChange.end(),
287 [](const auto& entry) { return entry.second; }))
288 {
289 return;
290 }
291
292 auto itHoldMax = std::max_element(_floorHolds.begin(), _floorHolds.end(),
293 [](const auto& aHold, const auto& bHold) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400294 return aHold.second < bHold.second;
295 });
Matt Spinler40554d82021-10-26 15:23:59 -0500296 if (itHoldMax == _floorHolds.end())
297 {
298 if (_floor != _defaultFloor)
299 {
300 FlightRecorder::instance().log(
301 "zone-floor"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500302 std::format("No set floor exists, using default floor",
Matt Spinler40554d82021-10-26 15:23:59 -0500303 _defaultFloor));
304 }
305 _floor = _defaultFloor;
306 }
307 else
308 {
309 if (_floor != itHoldMax->second)
310 {
311 FlightRecorder::instance().log(
312 "zone-floor"s + getName(),
Patrick Williamsfbf47032023-07-17 12:27:34 -0500313 std::format("Setting new floor to {}", itHoldMax->second));
Matt Spinler40554d82021-10-26 15:23:59 -0500314 }
315 _floor = itHoldMax->second;
316 }
317
318 // Floor above target, update target to floor
319 if (_target < _floor)
320 {
321 requestIncrease(_floor - _target);
322 }
323}
324
Matthew Barth12cb1252021-03-08 16:47:30 -0600325void Zone::setFloor(uint64_t target)
326{
327 // Check all entries are set to allow floor to be set
Matthew Barth8ba715e2021-03-05 09:00:05 -0600328 auto pred = [](const auto& entry) { return entry.second; };
Matthew Barth12cb1252021-03-08 16:47:30 -0600329 if (std::all_of(_floorChange.begin(), _floorChange.end(), pred))
330 {
Matthew Barthdc3152d2022-03-16 14:43:13 -0500331 _floor = (target > _ceiling) ? _ceiling : target;
Matthew Barth12cb1252021-03-08 16:47:30 -0600332 // Floor above target, update target to floor
333 if (_target < _floor)
334 {
335 requestIncrease(_floor - _target);
336 }
337 }
338}
339
340void Zone::requestIncrease(uint64_t targetDelta)
341{
Matthew Barth2b3253e2021-03-09 14:51:16 -0600342 // Only increase when delta is higher than the current increase delta for
343 // the zone and currently under ceiling
344 if (targetDelta > _incDelta && _target < _ceiling)
345 {
346 auto requestTarget = getRequestTargetBase();
347 requestTarget = (targetDelta - _incDelta) + requestTarget;
348 _incDelta = targetDelta;
349 // Target can not go above a current ceiling
350 if (requestTarget > _ceiling)
351 {
352 requestTarget = _ceiling;
353 }
Matthew Barth8ba715e2021-03-05 09:00:05 -0600354 setTarget(requestTarget);
Matthew Barth007de092021-03-25 13:56:04 -0500355 // Restart timer countdown for fan target increase
356 _incTimer.restartOnce(_incDelay);
Matthew Barth2b3253e2021-03-09 14:51:16 -0600357 }
Matthew Barth12cb1252021-03-08 16:47:30 -0600358}
359
Matthew Barth007de092021-03-25 13:56:04 -0500360void Zone::incTimerExpired()
361{
362 // Clear increase delta when timer expires allowing additional target
363 // increase requests or target decreases to occur
364 _incDelta = 0;
365}
366
Matthew Barth45c44ea2021-03-03 13:16:14 -0600367void Zone::requestDecrease(uint64_t targetDelta)
368{
369 // Only decrease the lowest target delta requested
370 if (_decDelta == 0 || targetDelta < _decDelta)
371 {
372 _decDelta = targetDelta;
373 }
374}
375
Matthew Barth007de092021-03-25 13:56:04 -0500376void Zone::decTimerExpired()
377{
378 // Check all entries are set to allow a decrease
Patrick Williams61b73292023-05-10 07:50:12 -0500379 auto pred = [](const auto& entry) { return entry.second; };
Matthew Barth007de092021-03-25 13:56:04 -0500380 auto decAllowed = std::all_of(_decAllowed.begin(), _decAllowed.end(), pred);
381
382 // Only decrease targets when allowed, a requested decrease target delta
383 // exists, where no requested increases exist and the increase timer is not
384 // running (i.e. not in the middle of increasing)
385 if (decAllowed && _decDelta != 0 && _incDelta == 0 &&
386 !_incTimer.isEnabled())
387 {
388 auto requestTarget = getRequestTargetBase();
389 // Request target should not start above ceiling
390 if (requestTarget > _ceiling)
391 {
392 requestTarget = _ceiling;
393 }
394 // Target can not go below the defined floor
395 if ((requestTarget < _decDelta) || (requestTarget - _decDelta < _floor))
396 {
397 requestTarget = _floor;
398 }
399 else
400 {
401 requestTarget = requestTarget - _decDelta;
402 }
403 setTarget(requestTarget);
404 }
405 // Clear decrease delta when timer expires
406 _decDelta = 0;
407 // Decrease timer is restarted since its repeating
408}
409
Matthew Bartha0dd1352021-03-09 11:10:49 -0600410void Zone::setPersisted(const std::string& intf, const std::string& prop)
411{
412 if (std::find_if(_propsPersisted[intf].begin(), _propsPersisted[intf].end(),
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500413 [&prop](const auto& p) { return prop == p; }) ==
Matthew Bartha0dd1352021-03-09 11:10:49 -0600414 _propsPersisted[intf].end())
415 {
416 _propsPersisted[intf].emplace_back(prop);
417 }
418}
419
Matthew Barth279183f2021-05-25 10:19:43 -0500420bool Zone::isPersisted(const std::string& intf, const std::string& prop) const
421{
422 auto it = _propsPersisted.find(intf);
423 if (it == _propsPersisted.end())
424 {
425 return false;
426 }
427
428 return std::any_of(it->second.begin(), it->second.end(),
429 [&prop](const auto& p) { return prop == p; });
430}
431
Matthew Barthab8e4b82021-05-27 13:25:46 -0500432void Zone::setPowerOnTarget(const json& jsonObj)
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500433{
Matthew Barth12e888f2021-08-20 11:41:32 -0500434 if (!jsonObj.contains("poweron_target"))
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500435 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000436 lg2::error("Missing required zone's poweron target", "JSON",
437 jsonObj.dump());
438 throw std::runtime_error("Missing required zone's poweron target");
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500439 }
Matthew Barth12e888f2021-08-20 11:41:32 -0500440 _poweronTarget = jsonObj["poweron_target"].get<uint64_t>();
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500441}
442
Matthew Barth651f03a2020-08-27 16:15:11 -0500443void Zone::setInterfaces(const json& jsonObj)
444{
445 for (const auto& interface : jsonObj["interfaces"])
446 {
447 if (!interface.contains("name") || !interface.contains("properties"))
448 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000449 lg2::error(
450 "Missing required zone interface attributes 'name, properties'",
451 "JSON", interface.dump());
Matthew Barth651f03a2020-08-27 16:15:11 -0500452 throw std::runtime_error(
453 "Missing required zone interface attributes");
454 }
Matthew Barth216229c2020-09-24 13:47:33 -0500455 auto propFuncs =
456 _intfPropHandlers.find(interface["name"].get<std::string>());
457 if (propFuncs == _intfPropHandlers.end())
458 {
459 // Construct list of available configurable interfaces
Patrick Williams5e15c3b2023-10-20 11:18:11 -0500460 auto intfs = std::accumulate(
461 std::next(_intfPropHandlers.begin()), _intfPropHandlers.end(),
462 _intfPropHandlers.begin()->first, [](auto list, auto intf) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400463 return std::move(list) + ", " + intf.first;
464 });
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000465 lg2::error(
466 "Configured interface not available. Available interfaces are {AVAILABLE_INTFS}",
467 "JSON", interface.dump(), "AVAILABLE_INTFS", intfs);
Matthew Barth216229c2020-09-24 13:47:33 -0500468 throw std::runtime_error("Configured interface not available");
469 }
470
Matthew Barth651f03a2020-08-27 16:15:11 -0500471 for (const auto& property : interface["properties"])
472 {
473 if (!property.contains("name"))
474 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000475 lg2::error(
476 "Missing required interface property attributes 'name'",
477 "JSON", property.dump());
Matthew Barth651f03a2020-08-27 16:15:11 -0500478 throw std::runtime_error(
479 "Missing required interface property attributes");
480 }
481 // Attribute "persist" is optional, defaults to `false`
482 auto persist = false;
483 if (property.contains("persist"))
484 {
485 persist = property["persist"].get<bool>();
486 }
487 // Property name from JSON must exactly match supported
488 // index names to functions in property namespace
Matthew Barth216229c2020-09-24 13:47:33 -0500489 auto propFunc =
490 propFuncs->second.find(property["name"].get<std::string>());
491 if (propFunc == propFuncs->second.end())
Matthew Barth651f03a2020-08-27 16:15:11 -0500492 {
Matthew Barth216229c2020-09-24 13:47:33 -0500493 // Construct list of available configurable properties
Matthew Barth651f03a2020-08-27 16:15:11 -0500494 auto props = std::accumulate(
Matthew Barth216229c2020-09-24 13:47:33 -0500495 std::next(propFuncs->second.begin()),
496 propFuncs->second.end(), propFuncs->second.begin()->first,
497 [](auto list, auto prop) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400498 return std::move(list) + ", " + prop.first;
499 });
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000500 lg2::error(
501 "Configured property not available. Available properties are {AVAILABLE_PROPS}",
502 "JSON", property.dump(), "AVAILABLE_PROPS", props);
Matthew Barth651f03a2020-08-27 16:15:11 -0500503 throw std::runtime_error(
504 "Configured property function not available");
505 }
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500506
507 _propInitFunctions.emplace_back(
508 propFunc->second(property, persist));
Matthew Barth651f03a2020-08-27 16:15:11 -0500509 }
Matthew Barth651f03a2020-08-27 16:15:11 -0500510 }
511}
512
Matt Spinler9db6dd12021-10-29 16:10:08 -0500513json Zone::dump() const
514{
515 json output;
516
517 output["active"] = _isActive;
518 output["floor"] = _floor;
Matthew Barth077448a2021-12-02 20:38:15 -0600519 output["ceiling"] = _ceiling;
Matt Spinler9db6dd12021-10-29 16:10:08 -0500520 output["target"] = _target;
521 output["increase_delta"] = _incDelta;
522 output["decrease_delta"] = _decDelta;
523 output["power_on_target"] = _poweronTarget;
524 output["default_ceiling"] = _defaultCeiling;
525 output["default_floor"] = _defaultFloor;
526 output["increase_delay"] = _incDelay.count();
527 output["decrease_interval"] = _decInterval.count();
528 output["requested_target_base"] = _requestTargetBase;
529 output["floor_change"] = _floorChange;
530 output["decrease_allowed"] = _decAllowed;
531 output["persisted_props"] = _propsPersisted;
Matt Spinler40554d82021-10-26 15:23:59 -0500532 output["target_holds"] = _targetHolds;
533 output["floor_holds"] = _floorHolds;
Matt Spinler9db6dd12021-10-29 16:10:08 -0500534
Matt Spinler2541f992022-08-16 16:00:04 -0500535 std::map<std::string, std::vector<uint64_t>> lockedTargets;
536 for (const auto& fan : _fans)
537 {
538 const auto& locks = fan->getLockedTargets();
539 if (!locks.empty())
540 {
541 lockedTargets[fan->getName()] = locks;
542 }
543 }
544 output["target_locks"] = lockedTargets;
545
Matt Spinler9db6dd12021-10-29 16:10:08 -0500546 return output;
547}
548
Matthew Barth651f03a2020-08-27 16:15:11 -0500549/**
Matthew Barth216229c2020-09-24 13:47:33 -0500550 * Properties of interfaces supported by the zone configuration that return
Matthew Barthab8e4b82021-05-27 13:25:46 -0500551 * a handler function that sets the zone's property value(s) and persist
552 * state.
Matthew Barth651f03a2020-08-27 16:15:11 -0500553 */
554namespace zone::property
555{
Matthew Barthb584d812021-03-11 15:55:04 -0600556// Get a set property handler function for the configured values of the
557// "Supported" property
Patrick Williams4fa67aa2025-02-03 14:28:47 -0500558std::function<void(DBusZone&, Zone&)> supported(const json& jsonObj,
559 bool persist)
Matthew Barth651f03a2020-08-27 16:15:11 -0500560{
561 std::vector<std::string> values;
Matthew Barth216229c2020-09-24 13:47:33 -0500562 if (!jsonObj.contains("values"))
Matthew Barth651f03a2020-08-27 16:15:11 -0500563 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000564 lg2::error("No 'values' found for \"Supported\" property, "
565 "using an empty list",
566 "JSON", jsonObj.dump());
Matthew Barth216229c2020-09-24 13:47:33 -0500567 }
568 else
569 {
570 for (const auto& value : jsonObj["values"])
571 {
572 if (!value.contains("value"))
573 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000574 lg2::error("No 'value' found for \"Supported\" property "
575 "entry, skipping",
576 "JSON", value.dump());
Matthew Barth216229c2020-09-24 13:47:33 -0500577 }
578 else
579 {
580 values.emplace_back(value["value"].get<std::string>());
581 }
582 }
Matthew Barth651f03a2020-08-27 16:15:11 -0500583 }
584
Matthew Barthb584d812021-03-11 15:55:04 -0600585 return Zone::setProperty<std::vector<std::string>>(
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500586 DBusZone::thermalModeIntf, DBusZone::supportedProp,
587 &DBusZone::supported, std::move(values), persist);
Matthew Barth651f03a2020-08-27 16:15:11 -0500588}
589
Matthew Barthab8e4b82021-05-27 13:25:46 -0500590// Get a set property handler function for a configured value of the
591// "Current" property
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500592std::function<void(DBusZone&, Zone&)> current(const json& jsonObj, bool persist)
Matthew Barth651f03a2020-08-27 16:15:11 -0500593{
Matthew Barth216229c2020-09-24 13:47:33 -0500594 // Use default value for "Current" property if no "value" entry given
595 if (!jsonObj.contains("value"))
596 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000597 lg2::info("No 'value' found for \"Current\" property, "
598 "using default",
599 "JSON", jsonObj.dump());
Matthew Barthb584d812021-03-11 15:55:04 -0600600 // Set persist state of property
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500601 return Zone::setPropertyPersist(DBusZone::thermalModeIntf,
602 DBusZone::currentProp, persist);
Matthew Barth216229c2020-09-24 13:47:33 -0500603 }
604
Matthew Barthb584d812021-03-11 15:55:04 -0600605 return Zone::setProperty<std::string>(
Matthew Barthbc89a8a2021-05-25 15:42:58 -0500606 DBusZone::thermalModeIntf, DBusZone::currentProp, &DBusZone::current,
Matthew Barthb584d812021-03-11 15:55:04 -0600607 jsonObj["value"].get<std::string>(), persist);
Matthew Barth651f03a2020-08-27 16:15:11 -0500608}
Matthew Barthb584d812021-03-11 15:55:04 -0600609
Matthew Barth651f03a2020-08-27 16:15:11 -0500610} // namespace zone::property
611
Matthew Barth4f0d3b72020-08-27 14:32:15 -0500612} // namespace phosphor::fan::control::json