blob: 546dff0881f51c127629307f9377dc074da458e4 [file] [log] [blame]
James Feist7136a5a2018-07-19 09:52:05 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
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*/
Pete O_o765a6d82025-07-23 21:44:14 -070016#include "config.h"
Chaul Lya552fe22024-11-15 10:20:28 +000017
Patrick Venture7e952d92020-10-05 15:58:52 -070018#include "dbusconfiguration.hpp"
James Feist7136a5a2018-07-19 09:52:05 -070019
Patrick Venture07716592018-10-14 11:46:40 -070020#include "conf.hpp"
Patrick Ventureef1f8862020-08-17 09:34:35 -070021#include "dbushelper.hpp"
22#include "dbusutil.hpp"
Ed Tanousf8b6e552025-06-27 13:27:50 -070023#include "ec/stepwise.hpp"
James Feist0c8223b2019-05-08 15:33:33 -070024#include "util.hpp"
Patrick Venture07716592018-10-14 11:46:40 -070025
Ed Tanousf8b6e552025-06-27 13:27:50 -070026#include <systemd/sd-bus.h>
27
28#include <boost/asio/error.hpp>
James Feist1fe08952019-05-07 09:17:16 -070029#include <boost/asio/steady_timer.hpp>
Patrick Venturea83a3ec2020-08-04 09:52:05 -070030#include <sdbusplus/bus.hpp>
31#include <sdbusplus/bus/match.hpp>
32#include <sdbusplus/exception.hpp>
Ed Tanousf8b6e552025-06-27 13:27:50 -070033#include <sdbusplus/message.hpp>
34#include <sdbusplus/message/native_types.hpp>
Alexander Hansen1ec3d132025-10-27 17:19:50 +010035#include <xyz/openbmc_project/ObjectMapper/common.hpp>
Patrick Venturea83a3ec2020-08-04 09:52:05 -070036
37#include <algorithm>
Ed Tanousf8b6e552025-06-27 13:27:50 -070038#include <array>
James Feist64f072a2018-08-10 16:39:24 -070039#include <chrono>
Ed Tanousf8b6e552025-06-27 13:27:50 -070040#include <cstdint>
41#include <format>
James Feist7136a5a2018-07-19 09:52:05 -070042#include <iostream>
Ed Tanousf8b6e552025-06-27 13:27:50 -070043#include <limits>
James Feist1fe08952019-05-07 09:17:16 -070044#include <list>
Ed Tanousf8b6e552025-06-27 13:27:50 -070045#include <map>
46#include <stdexcept>
47#include <string>
48#include <tuple>
James Feist7136a5a2018-07-19 09:52:05 -070049#include <unordered_map>
Ed Tanousf8b6e552025-06-27 13:27:50 -070050#include <utility>
James Feist1f802f52019-02-08 13:51:43 -080051#include <variant>
Ed Tanousf8b6e552025-06-27 13:27:50 -070052#include <vector>
James Feist7136a5a2018-07-19 09:52:05 -070053
Alexander Hansen1ec3d132025-10-27 17:19:50 +010054using ObjectMapper = sdbusplus::common::xyz::openbmc_project::ObjectMapper;
55
Patrick Venturea0764872020-08-08 07:48:43 -070056namespace pid_control
57{
58
Patrick Venturee2ec0f62018-09-04 12:30:27 -070059constexpr const char* pidConfigurationInterface =
James Feist7136a5a2018-07-19 09:52:05 -070060 "xyz.openbmc_project.Configuration.Pid";
Patrick Venturee2ec0f62018-09-04 12:30:27 -070061constexpr const char* objectManagerInterface =
James Feist7136a5a2018-07-19 09:52:05 -070062 "org.freedesktop.DBus.ObjectManager";
Patrick Venturee2ec0f62018-09-04 12:30:27 -070063constexpr const char* pidZoneConfigurationInterface =
James Feist7136a5a2018-07-19 09:52:05 -070064 "xyz.openbmc_project.Configuration.Pid.Zone";
James Feist22c257a2018-08-31 14:07:12 -070065constexpr const char* stepwiseConfigurationInterface =
66 "xyz.openbmc_project.Configuration.Stepwise";
James Feistf0096a02019-02-21 11:25:22 -080067constexpr const char* thermalControlIface =
68 "xyz.openbmc_project.Control.ThermalMode";
Patrick Venturee2ec0f62018-09-04 12:30:27 -070069constexpr const char* sensorInterface = "xyz.openbmc_project.Sensor.Value";
Patrick Venture0911bfe2020-08-10 12:51:40 -070070constexpr const char* defaultPwmInterface =
71 "xyz.openbmc_project.Control.FanPwm";
James Feist7136a5a2018-07-19 09:52:05 -070072
James Feist991ebd82020-07-21 11:14:52 -070073using Association = std::tuple<std::string, std::string, std::string>;
74using Associations = std::vector<Association>;
75
James Feist5ec20272019-07-10 11:59:57 -070076namespace thresholds
77{
78constexpr const char* warningInterface =
79 "xyz.openbmc_project.Sensor.Threshold.Warning";
80constexpr const char* criticalInterface =
81 "xyz.openbmc_project.Sensor.Threshold.Critical";
82const std::array<const char*, 4> types = {"CriticalLow", "CriticalHigh",
83 "WarningLow", "WarningHigh"};
84
85} // namespace thresholds
86
James Feist7136a5a2018-07-19 09:52:05 -070087namespace dbus_configuration
88{
Jason Lingf3b04fd2020-07-24 09:33:04 -070089using SensorInterfaceType = std::pair<std::string, std::string>;
90
91inline std::string getSensorNameFromPath(const std::string& dbusPath)
92{
Ed Tanousd2768c52025-06-26 11:42:57 -070093 return dbusPath.substr(dbusPath.find_last_of('/') + 1);
Jason Lingf3b04fd2020-07-24 09:33:04 -070094}
95
96inline std::string sensorNameToDbusName(const std::string& sensorName)
97{
98 std::string retString = sensorName;
99 std::replace(retString.begin(), retString.end(), ' ', '_');
100 return retString;
101}
James Feist5ec20272019-07-10 11:59:57 -0700102
Patrick Williamsb228bc32022-07-22 19:26:56 -0500103std::vector<std::string> getSelectedProfiles(sdbusplus::bus_t& bus)
James Feistf0096a02019-02-21 11:25:22 -0800104{
105 std::vector<std::string> ret;
Alexander Hansen1ec3d132025-10-27 17:19:50 +0100106 auto mapper = bus.new_method_call(
107 ObjectMapper::default_service, ObjectMapper::instance_path,
108 ObjectMapper::interface, ObjectMapper::method_names::get_sub_tree);
James Feistf0096a02019-02-21 11:25:22 -0800109 mapper.append("/", 0, std::array<const char*, 1>{thermalControlIface});
110 std::unordered_map<
111 std::string, std::unordered_map<std::string, std::vector<std::string>>>
112 respData;
113
114 try
115 {
116 auto resp = bus.call(mapper);
117 resp.read(respData);
118 }
Patrick Williams0001ee02021-10-06 14:44:22 -0500119 catch (const sdbusplus::exception_t&)
James Feistf0096a02019-02-21 11:25:22 -0800120 {
121 // can't do anything without mapper call data
122 throw std::runtime_error("ObjectMapper Call Failure");
123 }
124 if (respData.empty())
125 {
126 // if the user has profiles but doesn't expose the interface to select
127 // one, just go ahead without using profiles
128 return ret;
129 }
130
131 // assumption is that we should only have a small handful of selected
132 // profiles at a time (probably only 1), so calling each individually should
133 // not incur a large cost
134 for (const auto& objectPair : respData)
135 {
136 const std::string& path = objectPair.first;
137 for (const auto& ownerPair : objectPair.second)
138 {
139 const std::string& busName = ownerPair.first;
140 auto getProfile =
141 bus.new_method_call(busName.c_str(), path.c_str(),
142 "org.freedesktop.DBus.Properties", "Get");
143 getProfile.append(thermalControlIface, "Current");
144 std::variant<std::string> variantResp;
145 try
146 {
147 auto resp = bus.call(getProfile);
148 resp.read(variantResp);
149 }
Patrick Williams0001ee02021-10-06 14:44:22 -0500150 catch (const sdbusplus::exception_t&)
James Feistf0096a02019-02-21 11:25:22 -0800151 {
152 throw std::runtime_error("Failure getting profile");
153 }
154 std::string mode = std::get<std::string>(variantResp);
155 ret.emplace_back(std::move(mode));
156 }
157 }
Patrick Venture39199b42020-10-08 14:40:29 -0700158 if constexpr (pid_control::conf::DEBUG)
James Feistf0096a02019-02-21 11:25:22 -0800159 {
160 std::cout << "Profiles selected: ";
161 for (const auto& profile : ret)
162 {
163 std::cout << profile << " ";
164 }
165 std::cout << "\n";
166 }
167 return ret;
168}
169
James Feist991ebd82020-07-21 11:14:52 -0700170int eventHandler(sd_bus_message* m, void* context, sd_bus_error*)
James Feist7136a5a2018-07-19 09:52:05 -0700171{
James Feist991ebd82020-07-21 11:14:52 -0700172 if (context == nullptr || m == nullptr)
James Feist1fe08952019-05-07 09:17:16 -0700173 {
174 throw std::runtime_error("Invalid match");
175 }
James Feist991ebd82020-07-21 11:14:52 -0700176
177 // we skip associations because the mapper populates these, not the sensors
Josh Lehan10e46ef2023-02-01 18:25:58 -0800178 const std::array<const char*, 2> skipList = {
179 "xyz.openbmc_project.Association",
180 "xyz.openbmc_project.Association.Definitions"};
James Feist991ebd82020-07-21 11:14:52 -0700181
Patrick Williamsb228bc32022-07-22 19:26:56 -0500182 sdbusplus::message_t message(m);
James Feist991ebd82020-07-21 11:14:52 -0700183 if (std::string(message.get_member()) == "InterfacesAdded")
184 {
185 sdbusplus::message::object_path path;
186 std::unordered_map<
187 std::string,
188 std::unordered_map<std::string, std::variant<Associations, bool>>>
189 data;
190
191 message.read(path, data);
192
193 for (const char* skip : skipList)
194 {
195 auto find = data.find(skip);
196 if (find != data.end())
197 {
198 data.erase(find);
199 if (data.empty())
200 {
201 return 1;
202 }
203 }
204 }
Josh Lehan10e46ef2023-02-01 18:25:58 -0800205
206 if constexpr (pid_control::conf::DEBUG)
207 {
208 std::cout << "New config detected: " << path.str << std::endl;
209 for (auto& d : data)
210 {
211 std::cout << "\tdata is " << d.first << std::endl;
212 for (auto& second : d.second)
213 {
214 std::cout << "\t\tdata is " << second.first << std::endl;
215 }
216 }
217 }
James Feist991ebd82020-07-21 11:14:52 -0700218 }
219
James Feist1fe08952019-05-07 09:17:16 -0700220 boost::asio::steady_timer* timer =
221 static_cast<boost::asio::steady_timer*>(context);
222
223 // do a brief sleep as we tend to get a bunch of these events at
224 // once
225 timer->expires_after(std::chrono::seconds(2));
226 timer->async_wait([](const boost::system::error_code ec) {
227 if (ec == boost::asio::error::operation_aborted)
228 {
229 /* another timer started*/
230 return;
231 }
232
233 std::cout << "New configuration detected, reloading\n.";
Yong Li298a95c2020-04-07 15:11:02 +0800234 tryRestartControlLoops();
James Feist1fe08952019-05-07 09:17:16 -0700235 });
236
237 return 1;
238}
239
Patrick Williamsb228bc32022-07-22 19:26:56 -0500240void createMatches(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer)
James Feist1fe08952019-05-07 09:17:16 -0700241{
242 // this is a list because the matches can't be moved
Patrick Williamsb228bc32022-07-22 19:26:56 -0500243 static std::list<sdbusplus::bus::match_t> matches;
James Feist1fe08952019-05-07 09:17:16 -0700244
James Feist3987c8b2019-05-13 10:43:17 -0700245 const std::array<std::string, 4> interfaces = {
246 thermalControlIface, pidConfigurationInterface,
247 pidZoneConfigurationInterface, stepwiseConfigurationInterface};
James Feist1fe08952019-05-07 09:17:16 -0700248
249 // this list only needs to be created once
250 if (!matches.empty())
251 {
252 return;
253 }
254
255 // we restart when the configuration changes or there are new sensors
256 for (const auto& interface : interfaces)
257 {
258 matches.emplace_back(
259 bus,
260 "type='signal',member='PropertiesChanged',arg0namespace='" +
261 interface + "'",
262 eventHandler, &timer);
263 }
264 matches.emplace_back(
265 bus,
266 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
267 "sensors/'",
268 eventHandler, &timer);
Jinliang Wangc2a311b2023-04-26 18:36:56 +0000269 matches.emplace_back(bus,
270 "type='signal',member='InterfacesRemoved',arg0path='/"
271 "xyz/openbmc_project/sensors/'",
272 eventHandler, &timer);
James Feist1fe08952019-05-07 09:17:16 -0700273}
274
Jason Ling6fc301f2020-07-23 12:39:57 -0700275/**
276 * retrieve an attribute from the pid configuration map
277 * @param[in] base - the PID configuration map, keys are the attributes and
278 * value is the variant associated with that attribute.
279 * @param attributeName - the name of the attribute
280 * @return a variant holding the value associated with a key
281 * @throw runtime_error : attributeName is not in base
282 */
283inline DbusVariantType getPIDAttribute(
284 const std::unordered_map<std::string, DbusVariantType>& base,
285 const std::string& attributeName)
286{
287 auto search = base.find(attributeName);
288 if (search == base.end())
289 {
290 throw std::runtime_error("missing attribute " + attributeName);
291 }
292 return search->second;
293}
294
Harvey Wu239aa7d2022-11-18 08:43:34 +0800295inline void getCycleTimeSetting(
296 const std::unordered_map<std::string, DbusVariantType>& zone,
297 const int zoneIndex, const std::string& attributeName, uint64_t& value)
298{
299 auto findAttributeName = zone.find(attributeName);
300 if (findAttributeName != zone.end())
301 {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400302 double tmpAttributeValue =
303 std::visit(VariantToDoubleVisitor(), zone.at(attributeName));
Harvey Wu239aa7d2022-11-18 08:43:34 +0800304 if (tmpAttributeValue >= 1.0)
305 {
306 value = static_cast<uint64_t>(tmpAttributeValue);
307 }
308 else
309 {
310 std::cerr << "Zone " << zoneIndex << ": " << attributeName
311 << " is invalid. Use default " << value << " ms\n";
312 }
313 }
314 else
315 {
316 std::cerr << "Zone " << zoneIndex << ": " << attributeName
317 << " cannot find setting. Use default " << value << " ms\n";
318 }
319}
320
James Feist5ec20272019-07-10 11:59:57 -0700321void populatePidInfo(
Patrick Williamscd1e78a2025-04-07 17:21:05 -0400322 sdbusplus::bus_t& bus,
James Feist5ec20272019-07-10 11:59:57 -0700323 const std::unordered_map<std::string, DbusVariantType>& base,
Patrick Venture1df9e872020-10-08 15:35:01 -0700324 conf::ControllerInfo& info, const std::string* thresholdProperty,
Patrick Venture73823182020-10-08 15:12:51 -0700325 const std::map<std::string, conf::SensorConfig>& sensorConfig)
James Feist5ec20272019-07-10 11:59:57 -0700326{
Jason Ling6fc301f2020-07-23 12:39:57 -0700327 info.type = std::get<std::string>(getPIDAttribute(base, "Class"));
James Feist5ec20272019-07-10 11:59:57 -0700328 if (info.type == "fan")
329 {
330 info.setpoint = 0;
331 }
332 else
333 {
Jason Ling6fc301f2020-07-23 12:39:57 -0700334 info.setpoint = std::visit(VariantToDoubleVisitor(),
335 getPIDAttribute(base, "SetPoint"));
James Feist5ec20272019-07-10 11:59:57 -0700336 }
337
ykchiu9fe3a3c2023-05-11 13:43:54 +0800338 int failsafepercent = 0;
339 auto findFailSafe = base.find("FailSafePercent");
340 if (findFailSafe != base.end())
341 {
342 failsafepercent = std::visit(VariantToDoubleVisitor(),
343 getPIDAttribute(base, "FailSafePercent"));
344 }
345 info.failSafePercent = failsafepercent;
346
James Feist5ec20272019-07-10 11:59:57 -0700347 if (thresholdProperty != nullptr)
348 {
349 std::string interface;
350 if (*thresholdProperty == "WarningHigh" ||
351 *thresholdProperty == "WarningLow")
352 {
353 interface = thresholds::warningInterface;
354 }
355 else
356 {
357 interface = thresholds::criticalInterface;
358 }
Josh Lehan31058fd2023-01-13 11:06:16 -0800359
360 // Although this checks only the first vector element for the
361 // named threshold, it is OK, because the SetPointOffset parser
362 // splits up the input into individual vectors, each with only a
363 // single element, if it detects that SetPointOffset is in use.
364 const std::string& path =
365 sensorConfig.at(info.inputs.front().name).readPath;
James Feist5ec20272019-07-10 11:59:57 -0700366
Patrick Williamscd1e78a2025-04-07 17:21:05 -0400367 DbusHelper helper(bus);
Patrick Venture9b936922020-08-10 11:28:39 -0700368 std::string service = helper.getService(interface, path);
James Feist5ec20272019-07-10 11:59:57 -0700369 double reading = 0;
370 try
371 {
Patrick Venture9b936922020-08-10 11:28:39 -0700372 helper.getProperty(service, path, interface, *thresholdProperty,
373 reading);
James Feist5ec20272019-07-10 11:59:57 -0700374 }
Patrick Williamsb228bc32022-07-22 19:26:56 -0500375 catch (const sdbusplus::exception_t& ex)
James Feist5ec20272019-07-10 11:59:57 -0700376 {
377 // unsupported threshold, leaving reading at 0
378 }
379
380 info.setpoint += reading;
381 }
382
383 info.pidInfo.ts = 1.0; // currently unused
Jason Ling6fc301f2020-07-23 12:39:57 -0700384 info.pidInfo.proportionalCoeff = std::visit(
385 VariantToDoubleVisitor(), getPIDAttribute(base, "PCoefficient"));
386 info.pidInfo.integralCoeff = std::visit(
387 VariantToDoubleVisitor(), getPIDAttribute(base, "ICoefficient"));
Josh Lehanc612c052022-12-12 09:56:47 -0800388 // DCoefficient is below, it is optional, same reason as in buildjson.cpp
Jason Ling6fc301f2020-07-23 12:39:57 -0700389 info.pidInfo.feedFwdOffset = std::visit(
390 VariantToDoubleVisitor(), getPIDAttribute(base, "FFOffCoefficient"));
391 info.pidInfo.feedFwdGain = std::visit(
392 VariantToDoubleVisitor(), getPIDAttribute(base, "FFGainCoefficient"));
393 info.pidInfo.integralLimit.max = std::visit(
394 VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMax"));
395 info.pidInfo.integralLimit.min = std::visit(
396 VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMin"));
397 info.pidInfo.outLim.max = std::visit(VariantToDoubleVisitor(),
398 getPIDAttribute(base, "OutLimitMax"));
399 info.pidInfo.outLim.min = std::visit(VariantToDoubleVisitor(),
400 getPIDAttribute(base, "OutLimitMin"));
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400401 info.pidInfo.slewNeg =
402 std::visit(VariantToDoubleVisitor(), getPIDAttribute(base, "SlewNeg"));
403 info.pidInfo.slewPos =
404 std::visit(VariantToDoubleVisitor(), getPIDAttribute(base, "SlewPos"));
Josh Lehanc612c052022-12-12 09:56:47 -0800405
Delphine CC Chiu97889632023-11-06 11:32:46 +0800406 bool checkHysterWithSetpt = false;
James Feist5ec20272019-07-10 11:59:57 -0700407 double negativeHysteresis = 0;
408 double positiveHysteresis = 0;
Josh Lehanc612c052022-12-12 09:56:47 -0800409 double derivativeCoeff = 0;
James Feist5ec20272019-07-10 11:59:57 -0700410
Delphine CC Chiu5d897e22024-06-04 13:33:22 +0800411 auto findCheckHysterFlag = base.find("CheckHysteresisWithSetpoint");
James Feist5ec20272019-07-10 11:59:57 -0700412 auto findNeg = base.find("NegativeHysteresis");
413 auto findPos = base.find("PositiveHysteresis");
Josh Lehanc612c052022-12-12 09:56:47 -0800414 auto findDerivative = base.find("DCoefficient");
James Feist5ec20272019-07-10 11:59:57 -0700415
Delphine CC Chiu97889632023-11-06 11:32:46 +0800416 if (findCheckHysterFlag != base.end())
417 {
418 checkHysterWithSetpt = std::get<bool>(findCheckHysterFlag->second);
419 }
James Feist5ec20272019-07-10 11:59:57 -0700420 if (findNeg != base.end())
421 {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400422 negativeHysteresis =
423 std::visit(VariantToDoubleVisitor(), findNeg->second);
James Feist5ec20272019-07-10 11:59:57 -0700424 }
James Feist5ec20272019-07-10 11:59:57 -0700425 if (findPos != base.end())
426 {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400427 positiveHysteresis =
428 std::visit(VariantToDoubleVisitor(), findPos->second);
James Feist5ec20272019-07-10 11:59:57 -0700429 }
Josh Lehanc612c052022-12-12 09:56:47 -0800430 if (findDerivative != base.end())
431 {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400432 derivativeCoeff =
433 std::visit(VariantToDoubleVisitor(), findDerivative->second);
Josh Lehanc612c052022-12-12 09:56:47 -0800434 }
435
Delphine CC Chiu97889632023-11-06 11:32:46 +0800436 info.pidInfo.checkHysterWithSetpt = checkHysterWithSetpt;
James Feist5ec20272019-07-10 11:59:57 -0700437 info.pidInfo.negativeHysteresis = negativeHysteresis;
438 info.pidInfo.positiveHysteresis = positiveHysteresis;
Josh Lehanc612c052022-12-12 09:56:47 -0800439 info.pidInfo.derivativeCoeff = derivativeCoeff;
James Feist5ec20272019-07-10 11:59:57 -0700440}
441
Patrick Williamsb228bc32022-07-22 19:26:56 -0500442bool init(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer,
Patrick Venture73823182020-10-08 15:12:51 -0700443 std::map<std::string, conf::SensorConfig>& sensorConfig,
444 std::map<int64_t, conf::PIDConf>& zoneConfig,
445 std::map<int64_t, conf::ZoneConfig>& zoneDetailsConfig)
James Feist1fe08952019-05-07 09:17:16 -0700446{
James Feist1fe08952019-05-07 09:17:16 -0700447 sensorConfig.clear();
448 zoneConfig.clear();
449 zoneDetailsConfig.clear();
450
451 createMatches(bus, timer);
452
Alexander Hansen1ec3d132025-10-27 17:19:50 +0100453 auto mapper = bus.new_method_call(
454 ObjectMapper::default_service, ObjectMapper::instance_path,
455 ObjectMapper::interface, ObjectMapper::method_names::get_sub_tree);
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400456 mapper.append(
457 "/", 0,
458 std::array<const char*, 6>{
459 objectManagerInterface, pidConfigurationInterface,
460 pidZoneConfigurationInterface, stepwiseConfigurationInterface,
461 sensorInterface, defaultPwmInterface});
James Feist7136a5a2018-07-19 09:52:05 -0700462 std::unordered_map<
463 std::string, std::unordered_map<std::string, std::vector<std::string>>>
464 respData;
James Feist22c257a2018-08-31 14:07:12 -0700465 try
466 {
467 auto resp = bus.call(mapper);
James Feist22c257a2018-08-31 14:07:12 -0700468 resp.read(respData);
469 }
Patrick Williams0001ee02021-10-06 14:44:22 -0500470 catch (const sdbusplus::exception_t&)
James Feist22c257a2018-08-31 14:07:12 -0700471 {
472 // can't do anything without mapper call data
473 throw std::runtime_error("ObjectMapper Call Failure");
474 }
James Feist7136a5a2018-07-19 09:52:05 -0700475
James Feist7136a5a2018-07-19 09:52:05 -0700476 if (respData.empty())
477 {
James Feist22c257a2018-08-31 14:07:12 -0700478 // can't do anything without mapper call data
James Feist7136a5a2018-07-19 09:52:05 -0700479 throw std::runtime_error("No configuration data available from Mapper");
480 }
481 // create a map of pair of <has pid configuration, ObjectManager path>
482 std::unordered_map<std::string, std::pair<bool, std::string>> owners;
483 // and a map of <path, interface> for sensors
484 std::unordered_map<std::string, std::string> sensors;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700485 for (const auto& objectPair : respData)
James Feist7136a5a2018-07-19 09:52:05 -0700486 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700487 for (const auto& ownerPair : objectPair.second)
James Feist7136a5a2018-07-19 09:52:05 -0700488 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700489 auto& owner = owners[ownerPair.first];
490 for (const std::string& interface : ownerPair.second)
James Feist7136a5a2018-07-19 09:52:05 -0700491 {
James Feist7136a5a2018-07-19 09:52:05 -0700492 if (interface == objectManagerInterface)
493 {
494 owner.second = objectPair.first;
495 }
496 if (interface == pidConfigurationInterface ||
James Feist22c257a2018-08-31 14:07:12 -0700497 interface == pidZoneConfigurationInterface ||
498 interface == stepwiseConfigurationInterface)
James Feist7136a5a2018-07-19 09:52:05 -0700499 {
500 owner.first = true;
501 }
Patrick Venture0911bfe2020-08-10 12:51:40 -0700502 if (interface == sensorInterface ||
503 interface == defaultPwmInterface)
James Feist7136a5a2018-07-19 09:52:05 -0700504 {
505 // we're not interested in pwm sensors, just pwm control
506 if (interface == sensorInterface &&
507 objectPair.first.find("pwm") != std::string::npos)
508 {
509 continue;
510 }
511 sensors[objectPair.first] = interface;
512 }
513 }
514 }
515 }
516 ManagedObjectType configurations;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700517 for (const auto& owner : owners)
James Feist7136a5a2018-07-19 09:52:05 -0700518 {
519 // skip if no pid configuration (means probably a sensor)
520 if (!owner.second.first)
521 {
522 continue;
523 }
524 auto endpoint = bus.new_method_call(
525 owner.first.c_str(), owner.second.second.c_str(),
526 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist22c257a2018-08-31 14:07:12 -0700527 ManagedObjectType configuration;
528 try
James Feist7136a5a2018-07-19 09:52:05 -0700529 {
Manojkiran Eda7ca88872024-06-17 11:55:48 +0530530 auto response = bus.call(endpoint);
531 response.read(configuration);
James Feist22c257a2018-08-31 14:07:12 -0700532 }
Patrick Williams0001ee02021-10-06 14:44:22 -0500533 catch (const sdbusplus::exception_t&)
James Feist22c257a2018-08-31 14:07:12 -0700534 {
535 // this shouldn't happen, probably means daemon crashed
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400536 throw std::runtime_error(
537 "Error getting managed objects from " + owner.first);
James Feist7136a5a2018-07-19 09:52:05 -0700538 }
James Feist22c257a2018-08-31 14:07:12 -0700539
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700540 for (auto& pathPair : configuration)
James Feist7136a5a2018-07-19 09:52:05 -0700541 {
542 if (pathPair.second.find(pidConfigurationInterface) !=
543 pathPair.second.end() ||
544 pathPair.second.find(pidZoneConfigurationInterface) !=
James Feist22c257a2018-08-31 14:07:12 -0700545 pathPair.second.end() ||
546 pathPair.second.find(stepwiseConfigurationInterface) !=
James Feist7136a5a2018-07-19 09:52:05 -0700547 pathPair.second.end())
548 {
549 configurations.emplace(pathPair);
550 }
James Feistf0096a02019-02-21 11:25:22 -0800551 }
552 }
553
554 // remove controllers from config that aren't in the current profile(s)
James Feist3987c8b2019-05-13 10:43:17 -0700555 std::vector<std::string> selectedProfiles = getSelectedProfiles(bus);
556 if (selectedProfiles.size())
James Feistf0096a02019-02-21 11:25:22 -0800557 {
James Feist3987c8b2019-05-13 10:43:17 -0700558 for (auto pathIt = configurations.begin();
559 pathIt != configurations.end();)
James Feistf0096a02019-02-21 11:25:22 -0800560 {
James Feist3987c8b2019-05-13 10:43:17 -0700561 for (auto confIt = pathIt->second.begin();
562 confIt != pathIt->second.end();)
James Feistf0096a02019-02-21 11:25:22 -0800563 {
James Feist3987c8b2019-05-13 10:43:17 -0700564 auto profilesFind = confIt->second.find("Profiles");
565 if (profilesFind == confIt->second.end())
James Feistf0096a02019-02-21 11:25:22 -0800566 {
James Feist3987c8b2019-05-13 10:43:17 -0700567 confIt++;
568 continue; // if no profiles selected, apply always
569 }
570 auto profiles =
571 std::get<std::vector<std::string>>(profilesFind->second);
572 if (profiles.empty())
573 {
574 confIt++;
575 continue;
576 }
577
578 bool found = false;
579 for (const std::string& profile : profiles)
580 {
581 if (std::find(selectedProfiles.begin(),
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400582 selectedProfiles.end(), profile) !=
583 selectedProfiles.end())
James Feist3987c8b2019-05-13 10:43:17 -0700584 {
585 found = true;
586 break;
587 }
588 }
589 if (found)
590 {
591 confIt++;
James Feistf0096a02019-02-21 11:25:22 -0800592 }
593 else
594 {
James Feist3987c8b2019-05-13 10:43:17 -0700595 confIt = pathIt->second.erase(confIt);
James Feistf0096a02019-02-21 11:25:22 -0800596 }
597 }
James Feist3987c8b2019-05-13 10:43:17 -0700598 if (pathIt->second.empty())
James Feistf0096a02019-02-21 11:25:22 -0800599 {
James Feist3987c8b2019-05-13 10:43:17 -0700600 pathIt = configurations.erase(pathIt);
James Feistf0096a02019-02-21 11:25:22 -0800601 }
James Feist3987c8b2019-05-13 10:43:17 -0700602 else
James Feistf0096a02019-02-21 11:25:22 -0800603 {
James Feist3987c8b2019-05-13 10:43:17 -0700604 pathIt++;
James Feistf0096a02019-02-21 11:25:22 -0800605 }
James Feist7136a5a2018-07-19 09:52:05 -0700606 }
607 }
James Feist8c3c51e2018-08-08 16:31:43 -0700608
Josh Lehan998fbe62020-09-20 21:21:05 -0700609 // On D-Bus, although not necessary,
610 // having the "zoneID" field can still be useful,
611 // as it is used for diagnostic messages,
612 // logging file names, and so on.
613 // Accept optional "ZoneIndex" parameter to explicitly specify.
614 // If not present, or not unique, auto-assign index,
615 // using 0-based numbering, ensuring uniqueness.
616 std::map<std::string, int64_t> foundZones;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700617 for (const auto& configuration : configurations)
James Feist7136a5a2018-07-19 09:52:05 -0700618 {
619 auto findZone =
620 configuration.second.find(pidZoneConfigurationInterface);
621 if (findZone != configuration.second.end())
622 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700623 const auto& zone = findZone->second;
James Feistffd418b2018-11-15 14:46:36 -0800624
James Feist1f802f52019-02-08 13:51:43 -0800625 const std::string& name = std::get<std::string>(zone.at("Name"));
Josh Lehan998fbe62020-09-20 21:21:05 -0700626
627 auto findZoneIndex = zone.find("ZoneIndex");
628 if (findZoneIndex == zone.end())
629 {
630 continue;
631 }
632
633 auto ptrZoneIndex = std::get_if<double>(&(findZoneIndex->second));
634 if (!ptrZoneIndex)
635 {
636 continue;
637 }
638
639 auto desiredIndex = static_cast<int64_t>(*ptrZoneIndex);
640 auto grantedIndex = setZoneIndex(name, foundZones, desiredIndex);
641 std::cout << "Zone " << name << " is at ZoneIndex " << grantedIndex
642 << "\n";
643 }
644 }
645
646 for (const auto& configuration : configurations)
647 {
648 auto findZone =
649 configuration.second.find(pidZoneConfigurationInterface);
650 if (findZone != configuration.second.end())
651 {
652 const auto& zone = findZone->second;
653
654 const std::string& name = std::get<std::string>(zone.at("Name"));
655
656 auto index = getZoneIndex(name, foundZones);
James Feist8c3c51e2018-08-08 16:31:43 -0700657
Patrick Venturec54fbd82018-10-30 19:40:05 -0700658 auto& details = zoneDetailsConfig[index];
Josh Lehan998fbe62020-09-20 21:21:05 -0700659
James Feist3484bed2019-02-25 13:28:18 -0800660 details.minThermalOutput = std::visit(VariantToDoubleVisitor(),
661 zone.at("MinThermalOutput"));
ykchiu9fe3a3c2023-05-11 13:43:54 +0800662
663 int failsafepercent = 0;
664 auto findFailSafe = zone.find("FailSafePercent");
665 if (findFailSafe != zone.end())
666 {
667 failsafepercent = std::visit(VariantToDoubleVisitor(),
668 zone.at("FailSafePercent"));
669 }
670 details.failsafePercent = failsafepercent;
Josh Lehan9f9a06a2022-12-14 10:39:45 -0800671
Harvey Wu239aa7d2022-11-18 08:43:34 +0800672 getCycleTimeSetting(zone, index, "CycleIntervalTimeMS",
673 details.cycleTime.cycleIntervalTimeMS);
674 getCycleTimeSetting(zone, index, "UpdateThermalsTimeMS",
675 details.cycleTime.updateThermalsTimeMS);
Delphine CC Chiu97889632023-11-06 11:32:46 +0800676
677 bool accumulateSetPoint = false;
678 auto findAccSetPoint = zone.find("AccumulateSetPoint");
679 if (findAccSetPoint != zone.end())
680 {
681 accumulateSetPoint = std::get<bool>(findAccSetPoint->second);
682 }
683 details.accumulateSetPoint = accumulateSetPoint;
James Feist7136a5a2018-07-19 09:52:05 -0700684 }
685 auto findBase = configuration.second.find(pidConfigurationInterface);
Jason Lingf3b04fd2020-07-24 09:33:04 -0700686 // loop through all the PID configurations and fill out a sensor config
James Feist22c257a2018-08-31 14:07:12 -0700687 if (findBase != configuration.second.end())
James Feist7136a5a2018-07-19 09:52:05 -0700688 {
James Feist22c257a2018-08-31 14:07:12 -0700689 const auto& base =
690 configuration.second.at(pidConfigurationInterface);
ykchiu7c6d35d2023-05-10 17:01:46 +0800691 const std::string pidName =
692 sensorNameToDbusName(std::get<std::string>(base.at("Name")));
Jason Lingf3b04fd2020-07-24 09:33:04 -0700693 const std::string pidClass =
694 std::get<std::string>(base.at("Class"));
James Feist22c257a2018-08-31 14:07:12 -0700695 const std::vector<std::string>& zones =
James Feist1f802f52019-02-08 13:51:43 -0800696 std::get<std::vector<std::string>>(base.at("Zones"));
James Feist22c257a2018-08-31 14:07:12 -0700697 for (const std::string& zone : zones)
James Feist7136a5a2018-07-19 09:52:05 -0700698 {
Josh Lehan998fbe62020-09-20 21:21:05 -0700699 auto index = getZoneIndex(zone, foundZones);
700
James Feistf81f2882019-02-26 11:26:36 -0800701 conf::PIDConf& conf = zoneConfig[index];
Jason Lingf3b04fd2020-07-24 09:33:04 -0700702 std::vector<std::string> inputSensorNames(
703 std::get<std::vector<std::string>>(base.at("Inputs")));
704 std::vector<std::string> outputSensorNames;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800705 std::vector<std::string> missingAcceptableSensorNames;
Chaul Lya552fe22024-11-15 10:20:28 +0000706 std::vector<std::string> archivedInputSensorNames;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800707
708 auto findMissingAcceptable = base.find("MissingIsAcceptable");
709 if (findMissingAcceptable != base.end())
710 {
711 missingAcceptableSensorNames =
712 std::get<std::vector<std::string>>(
713 findMissingAcceptable->second);
714 }
James Feist50fdfe32018-09-24 15:51:09 -0700715
Jason Lingf3b04fd2020-07-24 09:33:04 -0700716 // assumption: all fan pids must have at least one output
717 if (pidClass == "fan")
James Feist50fdfe32018-09-24 15:51:09 -0700718 {
Jason Lingf3b04fd2020-07-24 09:33:04 -0700719 outputSensorNames = std::get<std::vector<std::string>>(
720 getPIDAttribute(base, "Outputs"));
James Feist50fdfe32018-09-24 15:51:09 -0700721 }
James Feist1738e2a2019-02-04 15:57:03 -0800722
Alex.Song8f73ad72021-10-07 00:18:27 +0800723 bool unavailableAsFailed = true;
724 auto findUnavailableAsFailed =
725 base.find("InputUnavailableAsFailed");
726 if (findUnavailableAsFailed != base.end())
727 {
728 unavailableAsFailed =
729 std::get<bool>(findUnavailableAsFailed->second);
730 }
731
Jason Lingf3b04fd2020-07-24 09:33:04 -0700732 std::vector<SensorInterfaceType> inputSensorInterfaces;
733 std::vector<SensorInterfaceType> outputSensorInterfaces;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800734 std::vector<SensorInterfaceType>
735 missingAcceptableSensorInterfaces;
736
Jason Lingf3b04fd2020-07-24 09:33:04 -0700737 /* populate an interface list for different sensor direction
738 * types (input,output)
739 */
740 /* take the Inputs from the configuration and generate
741 * a list of dbus descriptors (path, interface).
742 * Mapping can be many-to-one since an element of Inputs can be
743 * a regex
744 */
745 for (const std::string& sensorName : inputSensorNames)
James Feist50fdfe32018-09-24 15:51:09 -0700746 {
Chaul Lya552fe22024-11-15 10:20:28 +0000747#ifndef HANDLE_MISSING_OBJECT_PATHS
Jason Lingf3b04fd2020-07-24 09:33:04 -0700748 findSensors(sensors, sensorNameToDbusName(sensorName),
749 inputSensorInterfaces);
Chaul Lya552fe22024-11-15 10:20:28 +0000750#else
751 std::vector<std::pair<std::string, std::string>>
752 sensorPathIfacePairs;
753 auto found =
754 findSensors(sensors, sensorNameToDbusName(sensorName),
755 sensorPathIfacePairs);
756 if (found)
757 {
758 inputSensorInterfaces.insert(
759 inputSensorInterfaces.end(),
760 sensorPathIfacePairs.begin(),
761 sensorPathIfacePairs.end());
762 }
763 else if (pidClass != "fan")
764 {
765 if (std::find(missingAcceptableSensorNames.begin(),
766 missingAcceptableSensorNames.end(),
767 sensorName) ==
768 missingAcceptableSensorNames.end())
769 {
770 std::cerr
771 << "Pid controller: Missing a missing-unacceptable sensor from D-Bus "
772 << sensorName << "\n";
773 std::string inputSensorName =
774 sensorNameToDbusName(sensorName);
775 auto& config = sensorConfig[inputSensorName];
776 archivedInputSensorNames.push_back(inputSensorName);
777 config.type = pidClass;
778 config.readPath =
779 getSensorPath(config.type, inputSensorName);
780 config.timeout = 0;
781 config.ignoreDbusMinMax = true;
782 config.unavailableAsFailed = unavailableAsFailed;
783 }
784 else
785 {
786 // When an input sensor is NOT on DBus, and it's in
787 // the MissingIsAcceptable list. Ignore it and
788 // continue with the next input sensor.
789 std::cout
790 << "Pid controller: Missing a missing-acceptable sensor from D-Bus "
791 << sensorName << "\n";
792 continue;
793 }
794 }
795#endif
Jason Lingf3b04fd2020-07-24 09:33:04 -0700796 }
797 for (const std::string& sensorName : outputSensorNames)
798 {
799 findSensors(sensors, sensorNameToDbusName(sensorName),
800 outputSensorInterfaces);
James Feist1738e2a2019-02-04 15:57:03 -0800801 }
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800802 for (const std::string& sensorName :
803 missingAcceptableSensorNames)
804 {
805 findSensors(sensors, sensorNameToDbusName(sensorName),
806 missingAcceptableSensorInterfaces);
807 }
James Feist50fdfe32018-09-24 15:51:09 -0700808
Jason Lingf3b04fd2020-07-24 09:33:04 -0700809 for (const SensorInterfaceType& inputSensorInterface :
810 inputSensorInterfaces)
James Feist1738e2a2019-02-04 15:57:03 -0800811 {
Jason Lingf3b04fd2020-07-24 09:33:04 -0700812 const std::string& dbusInterface =
813 inputSensorInterface.second;
814 const std::string& inputSensorPath =
815 inputSensorInterface.first;
Josh Lehanfb82a872020-09-20 21:48:22 -0700816
817 // Setting timeout to 0 is intentional, as D-Bus passive
818 // sensor updates are pushed in, not pulled by timer poll.
819 // Setting ignoreDbusMinMax is intentional, as this
820 // prevents normalization of values to [0.0, 1.0] range,
821 // which would mess up the PID loop math.
822 // All non-fan PID classes should be initialized this way.
823 // As for why a fan should not use this code path, see
824 // the ed1dafdf168def37c65bfb7a5efd18d9dbe04727 commit.
Josh Lehan23e22b92022-11-12 22:37:58 -0800825 if ((pidClass == "temp") || (pidClass == "margin") ||
826 (pidClass == "power") || (pidClass == "powersum"))
James Feist50fdfe32018-09-24 15:51:09 -0700827 {
Harvey.Wued1dafd2022-02-09 13:53:20 +0800828 std::string inputSensorName =
829 getSensorNameFromPath(inputSensorPath);
830 auto& config = sensorConfig[inputSensorName];
Chaul Lya552fe22024-11-15 10:20:28 +0000831 archivedInputSensorNames.push_back(inputSensorName);
Harvey.Wued1dafd2022-02-09 13:53:20 +0800832 config.type = pidClass;
833 config.readPath = inputSensorInterface.first;
Jason Lingf3b04fd2020-07-24 09:33:04 -0700834 config.timeout = 0;
835 config.ignoreDbusMinMax = true;
Alex.Song8f73ad72021-10-07 00:18:27 +0800836 config.unavailableAsFailed = unavailableAsFailed;
James Feist50fdfe32018-09-24 15:51:09 -0700837 }
Josh Lehanfb82a872020-09-20 21:48:22 -0700838
Jason Lingf3b04fd2020-07-24 09:33:04 -0700839 if (dbusInterface != sensorInterface)
James Feist50fdfe32018-09-24 15:51:09 -0700840 {
Jason Lingf3b04fd2020-07-24 09:33:04 -0700841 /* all expected inputs in the configuration are expected
842 * to be sensor interfaces
843 */
Ed Tanous7d6e2252025-06-27 10:45:25 -0700844 throw std::runtime_error(std::format(
845 "sensor at dbus path [{}] has an interface [{}] that does not match the expected interface of {}",
846 inputSensorPath, dbusInterface, sensorInterface));
James Feist50fdfe32018-09-24 15:51:09 -0700847 }
848 }
James Feist1738e2a2019-02-04 15:57:03 -0800849
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800850 // MissingIsAcceptable same postprocessing as Inputs
851 missingAcceptableSensorNames.clear();
852 for (const SensorInterfaceType&
853 missingAcceptableSensorInterface :
854 missingAcceptableSensorInterfaces)
855 {
856 const std::string& dbusInterface =
857 missingAcceptableSensorInterface.second;
858 const std::string& missingAcceptableSensorPath =
859 missingAcceptableSensorInterface.first;
860
861 std::string missingAcceptableSensorName =
862 getSensorNameFromPath(missingAcceptableSensorPath);
863 missingAcceptableSensorNames.push_back(
864 missingAcceptableSensorName);
865
866 if (dbusInterface != sensorInterface)
867 {
868 /* MissingIsAcceptable same error checking as Inputs
869 */
Ed Tanous7d6e2252025-06-27 10:45:25 -0700870 throw std::runtime_error(std::format(
871 "sensor at dbus path [{}] has an interface [{}] that does not match the expected interface of {}",
872 missingAcceptableSensorPath, dbusInterface,
873 sensorInterface));
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800874 }
875 }
876
Jason Lingf3b04fd2020-07-24 09:33:04 -0700877 /* fan pids need to pair up tach sensors with their pwm
878 * counterparts
879 */
880 if (pidClass == "fan")
881 {
882 /* If a PID is a fan there should be either
883 * (1) one output(pwm) per input(tach)
884 * OR
885 * (2) one putput(pwm) for all inputs(tach)
886 * everything else indicates a bad configuration.
887 */
888 bool singlePwm = false;
889 if (outputSensorInterfaces.size() == 1)
890 {
891 /* one pwm, set write paths for all fan sensors to it */
892 singlePwm = true;
893 }
894 else if (inputSensorInterfaces.size() ==
895 outputSensorInterfaces.size())
896 {
897 /* one to one mapping, each fan sensor gets its own pwm
898 * control */
899 singlePwm = false;
900 }
901 else
902 {
903 throw std::runtime_error(
904 "fan PID has invalid number of Outputs");
905 }
906 std::string fanSensorName;
907 std::string pwmPath;
908 std::string pwmInterface;
Harvey.Wued1dafd2022-02-09 13:53:20 +0800909 std::string pwmSensorName;
Jason Lingf3b04fd2020-07-24 09:33:04 -0700910 if (singlePwm)
911 {
912 /* if just a single output(pwm) is provided then use
913 * that pwm control path for all the fan sensor write
914 * path configs
915 */
916 pwmPath = outputSensorInterfaces.at(0).first;
917 pwmInterface = outputSensorInterfaces.at(0).second;
918 }
919 for (uint32_t idx = 0; idx < inputSensorInterfaces.size();
920 idx++)
921 {
922 if (!singlePwm)
923 {
924 pwmPath = outputSensorInterfaces.at(idx).first;
925 pwmInterface =
926 outputSensorInterfaces.at(idx).second;
927 }
Patrick Venture0911bfe2020-08-10 12:51:40 -0700928 if (defaultPwmInterface != pwmInterface)
Jason Lingf3b04fd2020-07-24 09:33:04 -0700929 {
Ed Tanous7d6e2252025-06-27 10:45:25 -0700930 throw std::runtime_error(std::format(
931 "fan pwm control at dbus path [{}] has an interface [{}] that does not match the expected interface of {}",
932 pwmPath, pwmInterface, defaultPwmInterface));
Jason Lingf3b04fd2020-07-24 09:33:04 -0700933 }
934 const std::string& fanPath =
935 inputSensorInterfaces.at(idx).first;
936 fanSensorName = getSensorNameFromPath(fanPath);
Harvey.Wued1dafd2022-02-09 13:53:20 +0800937 pwmSensorName = getSensorNameFromPath(pwmPath);
938 std::string fanPwmIndex = fanSensorName + pwmSensorName;
Chaul Lya552fe22024-11-15 10:20:28 +0000939 archivedInputSensorNames.push_back(fanPwmIndex);
Harvey.Wued1dafd2022-02-09 13:53:20 +0800940 auto& fanConfig = sensorConfig[fanPwmIndex];
941 fanConfig.type = pidClass;
942 fanConfig.readPath = fanPath;
Jason Lingf3b04fd2020-07-24 09:33:04 -0700943 fanConfig.writePath = pwmPath;
944 // todo: un-hardcode this if there are fans with
945 // different ranges
946 fanConfig.max = 255;
947 fanConfig.min = 0;
948 }
949 }
James Feist11d243d2019-06-24 16:18:40 -0700950 // if the sensors aren't available in the current state, don't
951 // add them to the configuration.
Chaul Lya552fe22024-11-15 10:20:28 +0000952 if (archivedInputSensorNames.empty())
James Feist11d243d2019-06-24 16:18:40 -0700953 {
954 continue;
955 }
956
James Feist5ec20272019-07-10 11:59:57 -0700957 std::string offsetType;
James Feist50fdfe32018-09-24 15:51:09 -0700958
James Feist5ec20272019-07-10 11:59:57 -0700959 // SetPointOffset is a threshold value to pull from the sensor
960 // to apply an offset. For upper thresholds this means the
961 // setpoint is usually negative.
962 auto findSetpointOffset = base.find("SetPointOffset");
963 if (findSetpointOffset != base.end())
James Feist22c257a2018-08-31 14:07:12 -0700964 {
James Feist5ec20272019-07-10 11:59:57 -0700965 offsetType =
966 std::get<std::string>(findSetpointOffset->second);
967 if (std::find(thresholds::types.begin(),
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400968 thresholds::types.end(), offsetType) ==
969 thresholds::types.end())
James Feist5ec20272019-07-10 11:59:57 -0700970 {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400971 throw std::runtime_error(
972 "Unsupported type: " + offsetType);
James Feist5ec20272019-07-10 11:59:57 -0700973 }
974 }
975
Josh Lehan31058fd2023-01-13 11:06:16 -0800976 std::vector<double> inputTempToMargin;
977
978 auto findTempToMargin = base.find("TempToMargin");
979 if (findTempToMargin != base.end())
980 {
981 inputTempToMargin =
982 std::get<std::vector<double>>(findTempToMargin->second);
983 }
984
985 std::vector<pid_control::conf::SensorInput> sensorInputs =
Chaul Lya552fe22024-11-15 10:20:28 +0000986 spliceInputs(archivedInputSensorNames, inputTempToMargin,
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800987 missingAcceptableSensorNames);
Josh Lehan31058fd2023-01-13 11:06:16 -0800988
James Feist5ec20272019-07-10 11:59:57 -0700989 if (offsetType.empty())
990 {
ykchiu7c6d35d2023-05-10 17:01:46 +0800991 conf::ControllerInfo& info = conf[pidName];
Josh Lehan31058fd2023-01-13 11:06:16 -0800992 info.inputs = std::move(sensorInputs);
Patrick Venture73823182020-10-08 15:12:51 -0700993 populatePidInfo(bus, base, info, nullptr, sensorConfig);
James Feist22c257a2018-08-31 14:07:12 -0700994 }
995 else
996 {
James Feist5ec20272019-07-10 11:59:57 -0700997 // we have to split up the inputs, as in practice t-control
998 // values will differ, making setpoints differ
Josh Lehan31058fd2023-01-13 11:06:16 -0800999 for (const pid_control::conf::SensorInput& input :
1000 sensorInputs)
James Feist5ec20272019-07-10 11:59:57 -07001001 {
Josh Lehan31058fd2023-01-13 11:06:16 -08001002 conf::ControllerInfo& info = conf[input.name];
James Feist5ec20272019-07-10 11:59:57 -07001003 info.inputs.emplace_back(input);
Patrick Venture73823182020-10-08 15:12:51 -07001004 populatePidInfo(bus, base, info, &offsetType,
1005 sensorConfig);
James Feist5ec20272019-07-10 11:59:57 -07001006 }
James Feist22c257a2018-08-31 14:07:12 -07001007 }
James Feist22c257a2018-08-31 14:07:12 -07001008 }
1009 }
1010 auto findStepwise =
1011 configuration.second.find(stepwiseConfigurationInterface);
1012 if (findStepwise != configuration.second.end())
1013 {
1014 const auto& base = findStepwise->second;
ykchiu7c6d35d2023-05-10 17:01:46 +08001015 const std::string pidName =
1016 sensorNameToDbusName(std::get<std::string>(base.at("Name")));
James Feist22c257a2018-08-31 14:07:12 -07001017 const std::vector<std::string>& zones =
James Feist1f802f52019-02-08 13:51:43 -08001018 std::get<std::vector<std::string>>(base.at("Zones"));
James Feist22c257a2018-08-31 14:07:12 -07001019 for (const std::string& zone : zones)
1020 {
Josh Lehan998fbe62020-09-20 21:21:05 -07001021 auto index = getZoneIndex(zone, foundZones);
1022
James Feistf81f2882019-02-26 11:26:36 -08001023 conf::PIDConf& conf = zoneConfig[index];
James Feist50fdfe32018-09-24 15:51:09 -07001024
1025 std::vector<std::string> inputs;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001026 std::vector<std::string> missingAcceptableSensors;
1027 std::vector<std::string> missingAcceptableSensorNames;
James Feist50fdfe32018-09-24 15:51:09 -07001028 std::vector<std::string> sensorNames =
James Feist1f802f52019-02-08 13:51:43 -08001029 std::get<std::vector<std::string>>(base.at("Inputs"));
James Feist50fdfe32018-09-24 15:51:09 -07001030
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001031 auto findMissingAcceptable = base.find("MissingIsAcceptable");
1032 if (findMissingAcceptable != base.end())
1033 {
1034 missingAcceptableSensorNames =
1035 std::get<std::vector<std::string>>(
1036 findMissingAcceptable->second);
1037 }
1038
Alex.Song8f73ad72021-10-07 00:18:27 +08001039 bool unavailableAsFailed = true;
1040 auto findUnavailableAsFailed =
1041 base.find("InputUnavailableAsFailed");
1042 if (findUnavailableAsFailed != base.end())
1043 {
1044 unavailableAsFailed =
1045 std::get<bool>(findUnavailableAsFailed->second);
1046 }
1047
James Feist1738e2a2019-02-04 15:57:03 -08001048 bool sensorFound = false;
James Feist50fdfe32018-09-24 15:51:09 -07001049 for (const std::string& sensorName : sensorNames)
1050 {
James Feist1738e2a2019-02-04 15:57:03 -08001051 std::vector<std::pair<std::string, std::string>>
1052 sensorPathIfacePairs;
Jason Lingf3b04fd2020-07-24 09:33:04 -07001053 if (!findSensors(sensors, sensorNameToDbusName(sensorName),
1054 sensorPathIfacePairs))
James Feist50fdfe32018-09-24 15:51:09 -07001055 {
Chaul Lya552fe22024-11-15 10:20:28 +00001056#ifndef HANDLE_MISSING_OBJECT_PATHS
James Feist50fdfe32018-09-24 15:51:09 -07001057 break;
Chaul Lya552fe22024-11-15 10:20:28 +00001058#else
1059 if (std::find(missingAcceptableSensorNames.begin(),
1060 missingAcceptableSensorNames.end(),
1061 sensorName) ==
1062 missingAcceptableSensorNames.end())
1063 {
1064 // When an input sensor is NOT on DBus, and it's NOT
1065 // in the MissingIsAcceptable list. Build it as a
1066 // failed sensor with default information (temp
1067 // sensor path, temp type, ...)
1068 std::cerr
1069 << "Stepwise controller: Missing a missing-unacceptable sensor from D-Bus "
1070 << sensorName << "\n";
1071 std::string shortName =
1072 sensorNameToDbusName(sensorName);
1073
1074 inputs.push_back(shortName);
1075 auto& config = sensorConfig[shortName];
1076 config.type = "temp";
1077 config.readPath =
1078 getSensorPath(config.type, shortName);
1079 config.ignoreDbusMinMax = true;
1080 config.unavailableAsFailed = unavailableAsFailed;
1081 // todo: maybe un-hardcode this if we run into
1082 // slower timeouts with sensors
1083
1084 config.timeout = 0;
1085 sensorFound = true;
1086 }
1087 else
1088 {
1089 // When an input sensor is NOT on DBus, and it's in
1090 // the MissingIsAcceptable list. Ignore it and
1091 // continue with the next input sensor.
1092 std::cout
1093 << "Stepwise controller: Missing a missing-acceptable sensor from D-Bus "
1094 << sensorName << "\n";
1095 continue;
1096 }
1097#endif
James Feist50fdfe32018-09-24 15:51:09 -07001098 }
Chaul Lya552fe22024-11-15 10:20:28 +00001099 else
James Feist1738e2a2019-02-04 15:57:03 -08001100 {
Chaul Lya552fe22024-11-15 10:20:28 +00001101 for (const auto& sensorPathIfacePair :
1102 sensorPathIfacePairs)
1103 {
1104 std::string shortName = getSensorNameFromPath(
1105 sensorPathIfacePair.first);
James Feist50fdfe32018-09-24 15:51:09 -07001106
Chaul Lya552fe22024-11-15 10:20:28 +00001107 inputs.push_back(shortName);
1108 auto& config = sensorConfig[shortName];
1109 config.readPath = sensorPathIfacePair.first;
1110 config.type = "temp";
1111 config.ignoreDbusMinMax = true;
1112 config.unavailableAsFailed = unavailableAsFailed;
1113 // todo: maybe un-hardcode this if we run into
1114 // slower timeouts with sensors
James Feist1738e2a2019-02-04 15:57:03 -08001115
Chaul Lya552fe22024-11-15 10:20:28 +00001116 config.timeout = 0;
1117 sensorFound = true;
1118 }
James Feist1738e2a2019-02-04 15:57:03 -08001119 }
James Feist50fdfe32018-09-24 15:51:09 -07001120 }
1121 if (!sensorFound)
1122 {
1123 continue;
1124 }
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001125
1126 // MissingIsAcceptable same postprocessing as Inputs
1127 for (const std::string& missingAcceptableSensorName :
1128 missingAcceptableSensorNames)
1129 {
1130 std::vector<std::pair<std::string, std::string>>
1131 sensorPathIfacePairs;
1132 if (!findSensors(
1133 sensors,
1134 sensorNameToDbusName(missingAcceptableSensorName),
1135 sensorPathIfacePairs))
1136 {
Chaul Lya552fe22024-11-15 10:20:28 +00001137#ifndef HANDLE_MISSING_OBJECT_PATHS
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001138 break;
Chaul Lya552fe22024-11-15 10:20:28 +00001139#else
1140 // When a sensor in the MissingIsAcceptable list is NOT
1141 // on DBus and it still reaches here, which contradicts
1142 // to what we did in the Input sensor building step.
1143 // Continue.
1144 continue;
1145#endif
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001146 }
1147
1148 for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
1149 {
1150 std::string shortName =
1151 getSensorNameFromPath(sensorPathIfacePair.first);
1152
1153 missingAcceptableSensors.push_back(shortName);
1154 }
1155 }
1156
ykchiu7c6d35d2023-05-10 17:01:46 +08001157 conf::ControllerInfo& info = conf[pidName];
Josh Lehan31058fd2023-01-13 11:06:16 -08001158
1159 std::vector<double> inputTempToMargin;
1160
1161 auto findTempToMargin = base.find("TempToMargin");
1162 if (findTempToMargin != base.end())
1163 {
1164 inputTempToMargin =
1165 std::get<std::vector<double>>(findTempToMargin->second);
1166 }
1167
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001168 info.inputs = spliceInputs(inputs, inputTempToMargin,
1169 missingAcceptableSensors);
James Feist50fdfe32018-09-24 15:51:09 -07001170
James Feist22c257a2018-08-31 14:07:12 -07001171 info.type = "stepwise";
1172 info.stepwiseInfo.ts = 1.0; // currently unused
James Feist3dfaafd2018-09-20 15:46:58 -07001173 info.stepwiseInfo.positiveHysteresis = 0.0;
1174 info.stepwiseInfo.negativeHysteresis = 0.0;
James Feist608304d2019-02-25 10:01:42 -08001175 std::string subtype = std::get<std::string>(base.at("Class"));
1176
1177 info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
James Feist3dfaafd2018-09-20 15:46:58 -07001178 auto findPosHyst = base.find("PositiveHysteresis");
1179 auto findNegHyst = base.find("NegativeHysteresis");
1180 if (findPosHyst != base.end())
1181 {
James Feist1f802f52019-02-08 13:51:43 -08001182 info.stepwiseInfo.positiveHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -08001183 VariantToDoubleVisitor(), findPosHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -07001184 }
1185 if (findNegHyst != base.end())
1186 {
James Feist5782ab82019-04-02 08:38:48 -07001187 info.stepwiseInfo.negativeHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -08001188 VariantToDoubleVisitor(), findNegHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -07001189 }
James Feist22c257a2018-08-31 14:07:12 -07001190 std::vector<double> readings =
James Feist1f802f52019-02-08 13:51:43 -08001191 std::get<std::vector<double>>(base.at("Reading"));
James Feist22c257a2018-08-31 14:07:12 -07001192 if (readings.size() > ec::maxStepwisePoints)
1193 {
1194 throw std::invalid_argument("Too many stepwise points.");
1195 }
1196 if (readings.empty())
1197 {
1198 throw std::invalid_argument(
1199 "Must have one stepwise point.");
1200 }
1201 std::copy(readings.begin(), readings.end(),
1202 info.stepwiseInfo.reading);
1203 if (readings.size() < ec::maxStepwisePoints)
1204 {
1205 info.stepwiseInfo.reading[readings.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -08001206 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -07001207 }
1208 std::vector<double> outputs =
James Feist1f802f52019-02-08 13:51:43 -08001209 std::get<std::vector<double>>(base.at("Output"));
James Feist22c257a2018-08-31 14:07:12 -07001210 if (readings.size() != outputs.size())
1211 {
1212 throw std::invalid_argument(
1213 "Outputs size must match readings");
1214 }
1215 std::copy(outputs.begin(), outputs.end(),
1216 info.stepwiseInfo.output);
1217 if (outputs.size() < ec::maxStepwisePoints)
1218 {
1219 info.stepwiseInfo.output[outputs.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -08001220 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -07001221 }
James Feist7136a5a2018-07-19 09:52:05 -07001222 }
1223 }
1224 }
Patrick Venture39199b42020-10-08 14:40:29 -07001225 if constexpr (pid_control::conf::DEBUG)
James Feist7136a5a2018-07-19 09:52:05 -07001226 {
Patrick Venture39199b42020-10-08 14:40:29 -07001227 debugPrint(sensorConfig, zoneConfig, zoneDetailsConfig);
James Feist7136a5a2018-07-19 09:52:05 -07001228 }
James Feistc959c422018-11-01 12:33:40 -07001229 if (zoneConfig.empty() || zoneDetailsConfig.empty())
James Feist50fdfe32018-09-24 15:51:09 -07001230 {
James Feist1fe08952019-05-07 09:17:16 -07001231 std::cerr
1232 << "No fan zones, application pausing until new configuration\n";
1233 return false;
James Feist50fdfe32018-09-24 15:51:09 -07001234 }
James Feist1fe08952019-05-07 09:17:16 -07001235 return true;
James Feist7136a5a2018-07-19 09:52:05 -07001236}
Patrick Venturea0764872020-08-08 07:48:43 -07001237
James Feist7136a5a2018-07-19 09:52:05 -07001238} // namespace dbus_configuration
Patrick Venturea0764872020-08-08 07:48:43 -07001239} // namespace pid_control