blob: d86ebdea48d54e940f6feab8745762a752f6cd26 [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*/
Chaul Lya552fe22024-11-15 10:20:28 +000016#include "config.h"
17
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"
James Feist0c8223b2019-05-08 15:33:33 -070023#include "util.hpp"
Patrick Venture07716592018-10-14 11:46:40 -070024
James Feist1fe08952019-05-07 09:17:16 -070025#include <boost/asio/steady_timer.hpp>
Patrick Venturea83a3ec2020-08-04 09:52:05 -070026#include <sdbusplus/bus.hpp>
27#include <sdbusplus/bus/match.hpp>
28#include <sdbusplus/exception.hpp>
29
30#include <algorithm>
James Feist64f072a2018-08-10 16:39:24 -070031#include <chrono>
James Feist64f072a2018-08-10 16:39:24 -070032#include <functional>
James Feist7136a5a2018-07-19 09:52:05 -070033#include <iostream>
James Feist1fe08952019-05-07 09:17:16 -070034#include <list>
James Feist7136a5a2018-07-19 09:52:05 -070035#include <set>
36#include <unordered_map>
James Feist1f802f52019-02-08 13:51:43 -080037#include <variant>
James Feist7136a5a2018-07-19 09:52:05 -070038
Patrick Venturea0764872020-08-08 07:48:43 -070039namespace pid_control
40{
41
Patrick Venturee2ec0f62018-09-04 12:30:27 -070042constexpr const char* pidConfigurationInterface =
James Feist7136a5a2018-07-19 09:52:05 -070043 "xyz.openbmc_project.Configuration.Pid";
Patrick Venturee2ec0f62018-09-04 12:30:27 -070044constexpr const char* objectManagerInterface =
James Feist7136a5a2018-07-19 09:52:05 -070045 "org.freedesktop.DBus.ObjectManager";
Patrick Venturee2ec0f62018-09-04 12:30:27 -070046constexpr const char* pidZoneConfigurationInterface =
James Feist7136a5a2018-07-19 09:52:05 -070047 "xyz.openbmc_project.Configuration.Pid.Zone";
James Feist22c257a2018-08-31 14:07:12 -070048constexpr const char* stepwiseConfigurationInterface =
49 "xyz.openbmc_project.Configuration.Stepwise";
James Feistf0096a02019-02-21 11:25:22 -080050constexpr const char* thermalControlIface =
51 "xyz.openbmc_project.Control.ThermalMode";
Patrick Venturee2ec0f62018-09-04 12:30:27 -070052constexpr const char* sensorInterface = "xyz.openbmc_project.Sensor.Value";
Patrick Venture0911bfe2020-08-10 12:51:40 -070053constexpr const char* defaultPwmInterface =
54 "xyz.openbmc_project.Control.FanPwm";
James Feist7136a5a2018-07-19 09:52:05 -070055
James Feist991ebd82020-07-21 11:14:52 -070056using Association = std::tuple<std::string, std::string, std::string>;
57using Associations = std::vector<Association>;
58
James Feist5ec20272019-07-10 11:59:57 -070059namespace thresholds
60{
61constexpr const char* warningInterface =
62 "xyz.openbmc_project.Sensor.Threshold.Warning";
63constexpr const char* criticalInterface =
64 "xyz.openbmc_project.Sensor.Threshold.Critical";
65const std::array<const char*, 4> types = {"CriticalLow", "CriticalHigh",
66 "WarningLow", "WarningHigh"};
67
68} // namespace thresholds
69
James Feist7136a5a2018-07-19 09:52:05 -070070namespace dbus_configuration
71{
Jason Lingf3b04fd2020-07-24 09:33:04 -070072using SensorInterfaceType = std::pair<std::string, std::string>;
73
74inline std::string getSensorNameFromPath(const std::string& dbusPath)
75{
Ed Tanousd2768c52025-06-26 11:42:57 -070076 return dbusPath.substr(dbusPath.find_last_of('/') + 1);
Jason Lingf3b04fd2020-07-24 09:33:04 -070077}
78
79inline std::string sensorNameToDbusName(const std::string& sensorName)
80{
81 std::string retString = sensorName;
82 std::replace(retString.begin(), retString.end(), ' ', '_');
83 return retString;
84}
James Feist5ec20272019-07-10 11:59:57 -070085
Patrick Williamsb228bc32022-07-22 19:26:56 -050086std::vector<std::string> getSelectedProfiles(sdbusplus::bus_t& bus)
James Feistf0096a02019-02-21 11:25:22 -080087{
88 std::vector<std::string> ret;
Patrick Williamsbd63bca2024-08-16 15:21:10 -040089 auto mapper =
90 bus.new_method_call("xyz.openbmc_project.ObjectMapper",
91 "/xyz/openbmc_project/object_mapper",
92 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistf0096a02019-02-21 11:25:22 -080093 mapper.append("/", 0, std::array<const char*, 1>{thermalControlIface});
94 std::unordered_map<
95 std::string, std::unordered_map<std::string, std::vector<std::string>>>
96 respData;
97
98 try
99 {
100 auto resp = bus.call(mapper);
101 resp.read(respData);
102 }
Patrick Williams0001ee02021-10-06 14:44:22 -0500103 catch (const sdbusplus::exception_t&)
James Feistf0096a02019-02-21 11:25:22 -0800104 {
105 // can't do anything without mapper call data
106 throw std::runtime_error("ObjectMapper Call Failure");
107 }
108 if (respData.empty())
109 {
110 // if the user has profiles but doesn't expose the interface to select
111 // one, just go ahead without using profiles
112 return ret;
113 }
114
115 // assumption is that we should only have a small handful of selected
116 // profiles at a time (probably only 1), so calling each individually should
117 // not incur a large cost
118 for (const auto& objectPair : respData)
119 {
120 const std::string& path = objectPair.first;
121 for (const auto& ownerPair : objectPair.second)
122 {
123 const std::string& busName = ownerPair.first;
124 auto getProfile =
125 bus.new_method_call(busName.c_str(), path.c_str(),
126 "org.freedesktop.DBus.Properties", "Get");
127 getProfile.append(thermalControlIface, "Current");
128 std::variant<std::string> variantResp;
129 try
130 {
131 auto resp = bus.call(getProfile);
132 resp.read(variantResp);
133 }
Patrick Williams0001ee02021-10-06 14:44:22 -0500134 catch (const sdbusplus::exception_t&)
James Feistf0096a02019-02-21 11:25:22 -0800135 {
136 throw std::runtime_error("Failure getting profile");
137 }
138 std::string mode = std::get<std::string>(variantResp);
139 ret.emplace_back(std::move(mode));
140 }
141 }
Patrick Venture39199b42020-10-08 14:40:29 -0700142 if constexpr (pid_control::conf::DEBUG)
James Feistf0096a02019-02-21 11:25:22 -0800143 {
144 std::cout << "Profiles selected: ";
145 for (const auto& profile : ret)
146 {
147 std::cout << profile << " ";
148 }
149 std::cout << "\n";
150 }
151 return ret;
152}
153
James Feist991ebd82020-07-21 11:14:52 -0700154int eventHandler(sd_bus_message* m, void* context, sd_bus_error*)
James Feist7136a5a2018-07-19 09:52:05 -0700155{
James Feist991ebd82020-07-21 11:14:52 -0700156 if (context == nullptr || m == nullptr)
James Feist1fe08952019-05-07 09:17:16 -0700157 {
158 throw std::runtime_error("Invalid match");
159 }
James Feist991ebd82020-07-21 11:14:52 -0700160
161 // we skip associations because the mapper populates these, not the sensors
Josh Lehan10e46ef2023-02-01 18:25:58 -0800162 const std::array<const char*, 2> skipList = {
163 "xyz.openbmc_project.Association",
164 "xyz.openbmc_project.Association.Definitions"};
James Feist991ebd82020-07-21 11:14:52 -0700165
Patrick Williamsb228bc32022-07-22 19:26:56 -0500166 sdbusplus::message_t message(m);
James Feist991ebd82020-07-21 11:14:52 -0700167 if (std::string(message.get_member()) == "InterfacesAdded")
168 {
169 sdbusplus::message::object_path path;
170 std::unordered_map<
171 std::string,
172 std::unordered_map<std::string, std::variant<Associations, bool>>>
173 data;
174
175 message.read(path, data);
176
177 for (const char* skip : skipList)
178 {
179 auto find = data.find(skip);
180 if (find != data.end())
181 {
182 data.erase(find);
183 if (data.empty())
184 {
185 return 1;
186 }
187 }
188 }
Josh Lehan10e46ef2023-02-01 18:25:58 -0800189
190 if constexpr (pid_control::conf::DEBUG)
191 {
192 std::cout << "New config detected: " << path.str << std::endl;
193 for (auto& d : data)
194 {
195 std::cout << "\tdata is " << d.first << std::endl;
196 for (auto& second : d.second)
197 {
198 std::cout << "\t\tdata is " << second.first << std::endl;
199 }
200 }
201 }
James Feist991ebd82020-07-21 11:14:52 -0700202 }
203
James Feist1fe08952019-05-07 09:17:16 -0700204 boost::asio::steady_timer* timer =
205 static_cast<boost::asio::steady_timer*>(context);
206
207 // do a brief sleep as we tend to get a bunch of these events at
208 // once
209 timer->expires_after(std::chrono::seconds(2));
210 timer->async_wait([](const boost::system::error_code ec) {
211 if (ec == boost::asio::error::operation_aborted)
212 {
213 /* another timer started*/
214 return;
215 }
216
217 std::cout << "New configuration detected, reloading\n.";
Yong Li298a95c2020-04-07 15:11:02 +0800218 tryRestartControlLoops();
James Feist1fe08952019-05-07 09:17:16 -0700219 });
220
221 return 1;
222}
223
Patrick Williamsb228bc32022-07-22 19:26:56 -0500224void createMatches(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer)
James Feist1fe08952019-05-07 09:17:16 -0700225{
226 // this is a list because the matches can't be moved
Patrick Williamsb228bc32022-07-22 19:26:56 -0500227 static std::list<sdbusplus::bus::match_t> matches;
James Feist1fe08952019-05-07 09:17:16 -0700228
James Feist3987c8b2019-05-13 10:43:17 -0700229 const std::array<std::string, 4> interfaces = {
230 thermalControlIface, pidConfigurationInterface,
231 pidZoneConfigurationInterface, stepwiseConfigurationInterface};
James Feist1fe08952019-05-07 09:17:16 -0700232
233 // this list only needs to be created once
234 if (!matches.empty())
235 {
236 return;
237 }
238
239 // we restart when the configuration changes or there are new sensors
240 for (const auto& interface : interfaces)
241 {
242 matches.emplace_back(
243 bus,
244 "type='signal',member='PropertiesChanged',arg0namespace='" +
245 interface + "'",
246 eventHandler, &timer);
247 }
248 matches.emplace_back(
249 bus,
250 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
251 "sensors/'",
252 eventHandler, &timer);
Jinliang Wangc2a311b2023-04-26 18:36:56 +0000253 matches.emplace_back(bus,
254 "type='signal',member='InterfacesRemoved',arg0path='/"
255 "xyz/openbmc_project/sensors/'",
256 eventHandler, &timer);
James Feist1fe08952019-05-07 09:17:16 -0700257}
258
Jason Ling6fc301f2020-07-23 12:39:57 -0700259/**
260 * retrieve an attribute from the pid configuration map
261 * @param[in] base - the PID configuration map, keys are the attributes and
262 * value is the variant associated with that attribute.
263 * @param attributeName - the name of the attribute
264 * @return a variant holding the value associated with a key
265 * @throw runtime_error : attributeName is not in base
266 */
267inline DbusVariantType getPIDAttribute(
268 const std::unordered_map<std::string, DbusVariantType>& base,
269 const std::string& attributeName)
270{
271 auto search = base.find(attributeName);
272 if (search == base.end())
273 {
274 throw std::runtime_error("missing attribute " + attributeName);
275 }
276 return search->second;
277}
278
Harvey Wu239aa7d2022-11-18 08:43:34 +0800279inline void getCycleTimeSetting(
280 const std::unordered_map<std::string, DbusVariantType>& zone,
281 const int zoneIndex, const std::string& attributeName, uint64_t& value)
282{
283 auto findAttributeName = zone.find(attributeName);
284 if (findAttributeName != zone.end())
285 {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400286 double tmpAttributeValue =
287 std::visit(VariantToDoubleVisitor(), zone.at(attributeName));
Harvey Wu239aa7d2022-11-18 08:43:34 +0800288 if (tmpAttributeValue >= 1.0)
289 {
290 value = static_cast<uint64_t>(tmpAttributeValue);
291 }
292 else
293 {
294 std::cerr << "Zone " << zoneIndex << ": " << attributeName
295 << " is invalid. Use default " << value << " ms\n";
296 }
297 }
298 else
299 {
300 std::cerr << "Zone " << zoneIndex << ": " << attributeName
301 << " cannot find setting. Use default " << value << " ms\n";
302 }
303}
304
James Feist5ec20272019-07-10 11:59:57 -0700305void populatePidInfo(
Patrick Williamscd1e78a2025-04-07 17:21:05 -0400306 sdbusplus::bus_t& bus,
James Feist5ec20272019-07-10 11:59:57 -0700307 const std::unordered_map<std::string, DbusVariantType>& base,
Patrick Venture1df9e872020-10-08 15:35:01 -0700308 conf::ControllerInfo& info, const std::string* thresholdProperty,
Patrick Venture73823182020-10-08 15:12:51 -0700309 const std::map<std::string, conf::SensorConfig>& sensorConfig)
James Feist5ec20272019-07-10 11:59:57 -0700310{
Jason Ling6fc301f2020-07-23 12:39:57 -0700311 info.type = std::get<std::string>(getPIDAttribute(base, "Class"));
James Feist5ec20272019-07-10 11:59:57 -0700312 if (info.type == "fan")
313 {
314 info.setpoint = 0;
315 }
316 else
317 {
Jason Ling6fc301f2020-07-23 12:39:57 -0700318 info.setpoint = std::visit(VariantToDoubleVisitor(),
319 getPIDAttribute(base, "SetPoint"));
James Feist5ec20272019-07-10 11:59:57 -0700320 }
321
ykchiu9fe3a3c2023-05-11 13:43:54 +0800322 int failsafepercent = 0;
323 auto findFailSafe = base.find("FailSafePercent");
324 if (findFailSafe != base.end())
325 {
326 failsafepercent = std::visit(VariantToDoubleVisitor(),
327 getPIDAttribute(base, "FailSafePercent"));
328 }
329 info.failSafePercent = failsafepercent;
330
James Feist5ec20272019-07-10 11:59:57 -0700331 if (thresholdProperty != nullptr)
332 {
333 std::string interface;
334 if (*thresholdProperty == "WarningHigh" ||
335 *thresholdProperty == "WarningLow")
336 {
337 interface = thresholds::warningInterface;
338 }
339 else
340 {
341 interface = thresholds::criticalInterface;
342 }
Josh Lehan31058fd2023-01-13 11:06:16 -0800343
344 // Although this checks only the first vector element for the
345 // named threshold, it is OK, because the SetPointOffset parser
346 // splits up the input into individual vectors, each with only a
347 // single element, if it detects that SetPointOffset is in use.
348 const std::string& path =
349 sensorConfig.at(info.inputs.front().name).readPath;
James Feist5ec20272019-07-10 11:59:57 -0700350
Patrick Williamscd1e78a2025-04-07 17:21:05 -0400351 DbusHelper helper(bus);
Patrick Venture9b936922020-08-10 11:28:39 -0700352 std::string service = helper.getService(interface, path);
James Feist5ec20272019-07-10 11:59:57 -0700353 double reading = 0;
354 try
355 {
Patrick Venture9b936922020-08-10 11:28:39 -0700356 helper.getProperty(service, path, interface, *thresholdProperty,
357 reading);
James Feist5ec20272019-07-10 11:59:57 -0700358 }
Patrick Williamsb228bc32022-07-22 19:26:56 -0500359 catch (const sdbusplus::exception_t& ex)
James Feist5ec20272019-07-10 11:59:57 -0700360 {
361 // unsupported threshold, leaving reading at 0
362 }
363
364 info.setpoint += reading;
365 }
366
367 info.pidInfo.ts = 1.0; // currently unused
Jason Ling6fc301f2020-07-23 12:39:57 -0700368 info.pidInfo.proportionalCoeff = std::visit(
369 VariantToDoubleVisitor(), getPIDAttribute(base, "PCoefficient"));
370 info.pidInfo.integralCoeff = std::visit(
371 VariantToDoubleVisitor(), getPIDAttribute(base, "ICoefficient"));
Josh Lehanc612c052022-12-12 09:56:47 -0800372 // DCoefficient is below, it is optional, same reason as in buildjson.cpp
Jason Ling6fc301f2020-07-23 12:39:57 -0700373 info.pidInfo.feedFwdOffset = std::visit(
374 VariantToDoubleVisitor(), getPIDAttribute(base, "FFOffCoefficient"));
375 info.pidInfo.feedFwdGain = std::visit(
376 VariantToDoubleVisitor(), getPIDAttribute(base, "FFGainCoefficient"));
377 info.pidInfo.integralLimit.max = std::visit(
378 VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMax"));
379 info.pidInfo.integralLimit.min = std::visit(
380 VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMin"));
381 info.pidInfo.outLim.max = std::visit(VariantToDoubleVisitor(),
382 getPIDAttribute(base, "OutLimitMax"));
383 info.pidInfo.outLim.min = std::visit(VariantToDoubleVisitor(),
384 getPIDAttribute(base, "OutLimitMin"));
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400385 info.pidInfo.slewNeg =
386 std::visit(VariantToDoubleVisitor(), getPIDAttribute(base, "SlewNeg"));
387 info.pidInfo.slewPos =
388 std::visit(VariantToDoubleVisitor(), getPIDAttribute(base, "SlewPos"));
Josh Lehanc612c052022-12-12 09:56:47 -0800389
Delphine CC Chiu97889632023-11-06 11:32:46 +0800390 bool checkHysterWithSetpt = false;
James Feist5ec20272019-07-10 11:59:57 -0700391 double negativeHysteresis = 0;
392 double positiveHysteresis = 0;
Josh Lehanc612c052022-12-12 09:56:47 -0800393 double derivativeCoeff = 0;
James Feist5ec20272019-07-10 11:59:57 -0700394
Delphine CC Chiu5d897e22024-06-04 13:33:22 +0800395 auto findCheckHysterFlag = base.find("CheckHysteresisWithSetpoint");
James Feist5ec20272019-07-10 11:59:57 -0700396 auto findNeg = base.find("NegativeHysteresis");
397 auto findPos = base.find("PositiveHysteresis");
Josh Lehanc612c052022-12-12 09:56:47 -0800398 auto findDerivative = base.find("DCoefficient");
James Feist5ec20272019-07-10 11:59:57 -0700399
Delphine CC Chiu97889632023-11-06 11:32:46 +0800400 if (findCheckHysterFlag != base.end())
401 {
402 checkHysterWithSetpt = std::get<bool>(findCheckHysterFlag->second);
403 }
James Feist5ec20272019-07-10 11:59:57 -0700404 if (findNeg != base.end())
405 {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400406 negativeHysteresis =
407 std::visit(VariantToDoubleVisitor(), findNeg->second);
James Feist5ec20272019-07-10 11:59:57 -0700408 }
James Feist5ec20272019-07-10 11:59:57 -0700409 if (findPos != base.end())
410 {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400411 positiveHysteresis =
412 std::visit(VariantToDoubleVisitor(), findPos->second);
James Feist5ec20272019-07-10 11:59:57 -0700413 }
Josh Lehanc612c052022-12-12 09:56:47 -0800414 if (findDerivative != base.end())
415 {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400416 derivativeCoeff =
417 std::visit(VariantToDoubleVisitor(), findDerivative->second);
Josh Lehanc612c052022-12-12 09:56:47 -0800418 }
419
Delphine CC Chiu97889632023-11-06 11:32:46 +0800420 info.pidInfo.checkHysterWithSetpt = checkHysterWithSetpt;
James Feist5ec20272019-07-10 11:59:57 -0700421 info.pidInfo.negativeHysteresis = negativeHysteresis;
422 info.pidInfo.positiveHysteresis = positiveHysteresis;
Josh Lehanc612c052022-12-12 09:56:47 -0800423 info.pidInfo.derivativeCoeff = derivativeCoeff;
James Feist5ec20272019-07-10 11:59:57 -0700424}
425
Patrick Williamsb228bc32022-07-22 19:26:56 -0500426bool init(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer,
Patrick Venture73823182020-10-08 15:12:51 -0700427 std::map<std::string, conf::SensorConfig>& sensorConfig,
428 std::map<int64_t, conf::PIDConf>& zoneConfig,
429 std::map<int64_t, conf::ZoneConfig>& zoneDetailsConfig)
James Feist1fe08952019-05-07 09:17:16 -0700430{
James Feist1fe08952019-05-07 09:17:16 -0700431 sensorConfig.clear();
432 zoneConfig.clear();
433 zoneDetailsConfig.clear();
434
435 createMatches(bus, timer);
436
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400437 auto mapper =
438 bus.new_method_call("xyz.openbmc_project.ObjectMapper",
439 "/xyz/openbmc_project/object_mapper",
440 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
441 mapper.append(
442 "/", 0,
443 std::array<const char*, 6>{
444 objectManagerInterface, pidConfigurationInterface,
445 pidZoneConfigurationInterface, stepwiseConfigurationInterface,
446 sensorInterface, defaultPwmInterface});
James Feist7136a5a2018-07-19 09:52:05 -0700447 std::unordered_map<
448 std::string, std::unordered_map<std::string, std::vector<std::string>>>
449 respData;
James Feist22c257a2018-08-31 14:07:12 -0700450 try
451 {
452 auto resp = bus.call(mapper);
James Feist22c257a2018-08-31 14:07:12 -0700453 resp.read(respData);
454 }
Patrick Williams0001ee02021-10-06 14:44:22 -0500455 catch (const sdbusplus::exception_t&)
James Feist22c257a2018-08-31 14:07:12 -0700456 {
457 // can't do anything without mapper call data
458 throw std::runtime_error("ObjectMapper Call Failure");
459 }
James Feist7136a5a2018-07-19 09:52:05 -0700460
James Feist7136a5a2018-07-19 09:52:05 -0700461 if (respData.empty())
462 {
James Feist22c257a2018-08-31 14:07:12 -0700463 // can't do anything without mapper call data
James Feist7136a5a2018-07-19 09:52:05 -0700464 throw std::runtime_error("No configuration data available from Mapper");
465 }
466 // create a map of pair of <has pid configuration, ObjectManager path>
467 std::unordered_map<std::string, std::pair<bool, std::string>> owners;
468 // and a map of <path, interface> for sensors
469 std::unordered_map<std::string, std::string> sensors;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700470 for (const auto& objectPair : respData)
James Feist7136a5a2018-07-19 09:52:05 -0700471 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700472 for (const auto& ownerPair : objectPair.second)
James Feist7136a5a2018-07-19 09:52:05 -0700473 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700474 auto& owner = owners[ownerPair.first];
475 for (const std::string& interface : ownerPair.second)
James Feist7136a5a2018-07-19 09:52:05 -0700476 {
James Feist7136a5a2018-07-19 09:52:05 -0700477 if (interface == objectManagerInterface)
478 {
479 owner.second = objectPair.first;
480 }
481 if (interface == pidConfigurationInterface ||
James Feist22c257a2018-08-31 14:07:12 -0700482 interface == pidZoneConfigurationInterface ||
483 interface == stepwiseConfigurationInterface)
James Feist7136a5a2018-07-19 09:52:05 -0700484 {
485 owner.first = true;
486 }
Patrick Venture0911bfe2020-08-10 12:51:40 -0700487 if (interface == sensorInterface ||
488 interface == defaultPwmInterface)
James Feist7136a5a2018-07-19 09:52:05 -0700489 {
490 // we're not interested in pwm sensors, just pwm control
491 if (interface == sensorInterface &&
492 objectPair.first.find("pwm") != std::string::npos)
493 {
494 continue;
495 }
496 sensors[objectPair.first] = interface;
497 }
498 }
499 }
500 }
501 ManagedObjectType configurations;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700502 for (const auto& owner : owners)
James Feist7136a5a2018-07-19 09:52:05 -0700503 {
504 // skip if no pid configuration (means probably a sensor)
505 if (!owner.second.first)
506 {
507 continue;
508 }
509 auto endpoint = bus.new_method_call(
510 owner.first.c_str(), owner.second.second.c_str(),
511 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist22c257a2018-08-31 14:07:12 -0700512 ManagedObjectType configuration;
513 try
James Feist7136a5a2018-07-19 09:52:05 -0700514 {
Manojkiran Eda7ca88872024-06-17 11:55:48 +0530515 auto response = bus.call(endpoint);
516 response.read(configuration);
James Feist22c257a2018-08-31 14:07:12 -0700517 }
Patrick Williams0001ee02021-10-06 14:44:22 -0500518 catch (const sdbusplus::exception_t&)
James Feist22c257a2018-08-31 14:07:12 -0700519 {
520 // this shouldn't happen, probably means daemon crashed
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400521 throw std::runtime_error(
522 "Error getting managed objects from " + owner.first);
James Feist7136a5a2018-07-19 09:52:05 -0700523 }
James Feist22c257a2018-08-31 14:07:12 -0700524
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700525 for (auto& pathPair : configuration)
James Feist7136a5a2018-07-19 09:52:05 -0700526 {
527 if (pathPair.second.find(pidConfigurationInterface) !=
528 pathPair.second.end() ||
529 pathPair.second.find(pidZoneConfigurationInterface) !=
James Feist22c257a2018-08-31 14:07:12 -0700530 pathPair.second.end() ||
531 pathPair.second.find(stepwiseConfigurationInterface) !=
James Feist7136a5a2018-07-19 09:52:05 -0700532 pathPair.second.end())
533 {
534 configurations.emplace(pathPair);
535 }
James Feistf0096a02019-02-21 11:25:22 -0800536 }
537 }
538
539 // remove controllers from config that aren't in the current profile(s)
James Feist3987c8b2019-05-13 10:43:17 -0700540 std::vector<std::string> selectedProfiles = getSelectedProfiles(bus);
541 if (selectedProfiles.size())
James Feistf0096a02019-02-21 11:25:22 -0800542 {
James Feist3987c8b2019-05-13 10:43:17 -0700543 for (auto pathIt = configurations.begin();
544 pathIt != configurations.end();)
James Feistf0096a02019-02-21 11:25:22 -0800545 {
James Feist3987c8b2019-05-13 10:43:17 -0700546 for (auto confIt = pathIt->second.begin();
547 confIt != pathIt->second.end();)
James Feistf0096a02019-02-21 11:25:22 -0800548 {
James Feist3987c8b2019-05-13 10:43:17 -0700549 auto profilesFind = confIt->second.find("Profiles");
550 if (profilesFind == confIt->second.end())
James Feistf0096a02019-02-21 11:25:22 -0800551 {
James Feist3987c8b2019-05-13 10:43:17 -0700552 confIt++;
553 continue; // if no profiles selected, apply always
554 }
555 auto profiles =
556 std::get<std::vector<std::string>>(profilesFind->second);
557 if (profiles.empty())
558 {
559 confIt++;
560 continue;
561 }
562
563 bool found = false;
564 for (const std::string& profile : profiles)
565 {
566 if (std::find(selectedProfiles.begin(),
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400567 selectedProfiles.end(), profile) !=
568 selectedProfiles.end())
James Feist3987c8b2019-05-13 10:43:17 -0700569 {
570 found = true;
571 break;
572 }
573 }
574 if (found)
575 {
576 confIt++;
James Feistf0096a02019-02-21 11:25:22 -0800577 }
578 else
579 {
James Feist3987c8b2019-05-13 10:43:17 -0700580 confIt = pathIt->second.erase(confIt);
James Feistf0096a02019-02-21 11:25:22 -0800581 }
582 }
James Feist3987c8b2019-05-13 10:43:17 -0700583 if (pathIt->second.empty())
James Feistf0096a02019-02-21 11:25:22 -0800584 {
James Feist3987c8b2019-05-13 10:43:17 -0700585 pathIt = configurations.erase(pathIt);
James Feistf0096a02019-02-21 11:25:22 -0800586 }
James Feist3987c8b2019-05-13 10:43:17 -0700587 else
James Feistf0096a02019-02-21 11:25:22 -0800588 {
James Feist3987c8b2019-05-13 10:43:17 -0700589 pathIt++;
James Feistf0096a02019-02-21 11:25:22 -0800590 }
James Feist7136a5a2018-07-19 09:52:05 -0700591 }
592 }
James Feist8c3c51e2018-08-08 16:31:43 -0700593
Josh Lehan998fbe62020-09-20 21:21:05 -0700594 // On D-Bus, although not necessary,
595 // having the "zoneID" field can still be useful,
596 // as it is used for diagnostic messages,
597 // logging file names, and so on.
598 // Accept optional "ZoneIndex" parameter to explicitly specify.
599 // If not present, or not unique, auto-assign index,
600 // using 0-based numbering, ensuring uniqueness.
601 std::map<std::string, int64_t> foundZones;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700602 for (const auto& configuration : configurations)
James Feist7136a5a2018-07-19 09:52:05 -0700603 {
604 auto findZone =
605 configuration.second.find(pidZoneConfigurationInterface);
606 if (findZone != configuration.second.end())
607 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700608 const auto& zone = findZone->second;
James Feistffd418b2018-11-15 14:46:36 -0800609
James Feist1f802f52019-02-08 13:51:43 -0800610 const std::string& name = std::get<std::string>(zone.at("Name"));
Josh Lehan998fbe62020-09-20 21:21:05 -0700611
612 auto findZoneIndex = zone.find("ZoneIndex");
613 if (findZoneIndex == zone.end())
614 {
615 continue;
616 }
617
618 auto ptrZoneIndex = std::get_if<double>(&(findZoneIndex->second));
619 if (!ptrZoneIndex)
620 {
621 continue;
622 }
623
624 auto desiredIndex = static_cast<int64_t>(*ptrZoneIndex);
625 auto grantedIndex = setZoneIndex(name, foundZones, desiredIndex);
626 std::cout << "Zone " << name << " is at ZoneIndex " << grantedIndex
627 << "\n";
628 }
629 }
630
631 for (const auto& configuration : configurations)
632 {
633 auto findZone =
634 configuration.second.find(pidZoneConfigurationInterface);
635 if (findZone != configuration.second.end())
636 {
637 const auto& zone = findZone->second;
638
639 const std::string& name = std::get<std::string>(zone.at("Name"));
640
641 auto index = getZoneIndex(name, foundZones);
James Feist8c3c51e2018-08-08 16:31:43 -0700642
Patrick Venturec54fbd82018-10-30 19:40:05 -0700643 auto& details = zoneDetailsConfig[index];
Josh Lehan998fbe62020-09-20 21:21:05 -0700644
James Feist3484bed2019-02-25 13:28:18 -0800645 details.minThermalOutput = std::visit(VariantToDoubleVisitor(),
646 zone.at("MinThermalOutput"));
ykchiu9fe3a3c2023-05-11 13:43:54 +0800647
648 int failsafepercent = 0;
649 auto findFailSafe = zone.find("FailSafePercent");
650 if (findFailSafe != zone.end())
651 {
652 failsafepercent = std::visit(VariantToDoubleVisitor(),
653 zone.at("FailSafePercent"));
654 }
655 details.failsafePercent = failsafepercent;
Josh Lehan9f9a06a2022-12-14 10:39:45 -0800656
Harvey Wu239aa7d2022-11-18 08:43:34 +0800657 getCycleTimeSetting(zone, index, "CycleIntervalTimeMS",
658 details.cycleTime.cycleIntervalTimeMS);
659 getCycleTimeSetting(zone, index, "UpdateThermalsTimeMS",
660 details.cycleTime.updateThermalsTimeMS);
Delphine CC Chiu97889632023-11-06 11:32:46 +0800661
662 bool accumulateSetPoint = false;
663 auto findAccSetPoint = zone.find("AccumulateSetPoint");
664 if (findAccSetPoint != zone.end())
665 {
666 accumulateSetPoint = std::get<bool>(findAccSetPoint->second);
667 }
668 details.accumulateSetPoint = accumulateSetPoint;
James Feist7136a5a2018-07-19 09:52:05 -0700669 }
670 auto findBase = configuration.second.find(pidConfigurationInterface);
Jason Lingf3b04fd2020-07-24 09:33:04 -0700671 // loop through all the PID configurations and fill out a sensor config
James Feist22c257a2018-08-31 14:07:12 -0700672 if (findBase != configuration.second.end())
James Feist7136a5a2018-07-19 09:52:05 -0700673 {
James Feist22c257a2018-08-31 14:07:12 -0700674 const auto& base =
675 configuration.second.at(pidConfigurationInterface);
ykchiu7c6d35d2023-05-10 17:01:46 +0800676 const std::string pidName =
677 sensorNameToDbusName(std::get<std::string>(base.at("Name")));
Jason Lingf3b04fd2020-07-24 09:33:04 -0700678 const std::string pidClass =
679 std::get<std::string>(base.at("Class"));
James Feist22c257a2018-08-31 14:07:12 -0700680 const std::vector<std::string>& zones =
James Feist1f802f52019-02-08 13:51:43 -0800681 std::get<std::vector<std::string>>(base.at("Zones"));
James Feist22c257a2018-08-31 14:07:12 -0700682 for (const std::string& zone : zones)
James Feist7136a5a2018-07-19 09:52:05 -0700683 {
Josh Lehan998fbe62020-09-20 21:21:05 -0700684 auto index = getZoneIndex(zone, foundZones);
685
James Feistf81f2882019-02-26 11:26:36 -0800686 conf::PIDConf& conf = zoneConfig[index];
Jason Lingf3b04fd2020-07-24 09:33:04 -0700687 std::vector<std::string> inputSensorNames(
688 std::get<std::vector<std::string>>(base.at("Inputs")));
689 std::vector<std::string> outputSensorNames;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800690 std::vector<std::string> missingAcceptableSensorNames;
Chaul Lya552fe22024-11-15 10:20:28 +0000691 std::vector<std::string> archivedInputSensorNames;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800692
693 auto findMissingAcceptable = base.find("MissingIsAcceptable");
694 if (findMissingAcceptable != base.end())
695 {
696 missingAcceptableSensorNames =
697 std::get<std::vector<std::string>>(
698 findMissingAcceptable->second);
699 }
James Feist50fdfe32018-09-24 15:51:09 -0700700
Jason Lingf3b04fd2020-07-24 09:33:04 -0700701 // assumption: all fan pids must have at least one output
702 if (pidClass == "fan")
James Feist50fdfe32018-09-24 15:51:09 -0700703 {
Jason Lingf3b04fd2020-07-24 09:33:04 -0700704 outputSensorNames = std::get<std::vector<std::string>>(
705 getPIDAttribute(base, "Outputs"));
James Feist50fdfe32018-09-24 15:51:09 -0700706 }
James Feist1738e2a2019-02-04 15:57:03 -0800707
Alex.Song8f73ad72021-10-07 00:18:27 +0800708 bool unavailableAsFailed = true;
709 auto findUnavailableAsFailed =
710 base.find("InputUnavailableAsFailed");
711 if (findUnavailableAsFailed != base.end())
712 {
713 unavailableAsFailed =
714 std::get<bool>(findUnavailableAsFailed->second);
715 }
716
Jason Lingf3b04fd2020-07-24 09:33:04 -0700717 std::vector<SensorInterfaceType> inputSensorInterfaces;
718 std::vector<SensorInterfaceType> outputSensorInterfaces;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800719 std::vector<SensorInterfaceType>
720 missingAcceptableSensorInterfaces;
721
Jason Lingf3b04fd2020-07-24 09:33:04 -0700722 /* populate an interface list for different sensor direction
723 * types (input,output)
724 */
725 /* take the Inputs from the configuration and generate
726 * a list of dbus descriptors (path, interface).
727 * Mapping can be many-to-one since an element of Inputs can be
728 * a regex
729 */
730 for (const std::string& sensorName : inputSensorNames)
James Feist50fdfe32018-09-24 15:51:09 -0700731 {
Chaul Lya552fe22024-11-15 10:20:28 +0000732#ifndef HANDLE_MISSING_OBJECT_PATHS
Jason Lingf3b04fd2020-07-24 09:33:04 -0700733 findSensors(sensors, sensorNameToDbusName(sensorName),
734 inputSensorInterfaces);
Chaul Lya552fe22024-11-15 10:20:28 +0000735#else
736 std::vector<std::pair<std::string, std::string>>
737 sensorPathIfacePairs;
738 auto found =
739 findSensors(sensors, sensorNameToDbusName(sensorName),
740 sensorPathIfacePairs);
741 if (found)
742 {
743 inputSensorInterfaces.insert(
744 inputSensorInterfaces.end(),
745 sensorPathIfacePairs.begin(),
746 sensorPathIfacePairs.end());
747 }
748 else if (pidClass != "fan")
749 {
750 if (std::find(missingAcceptableSensorNames.begin(),
751 missingAcceptableSensorNames.end(),
752 sensorName) ==
753 missingAcceptableSensorNames.end())
754 {
755 std::cerr
756 << "Pid controller: Missing a missing-unacceptable sensor from D-Bus "
757 << sensorName << "\n";
758 std::string inputSensorName =
759 sensorNameToDbusName(sensorName);
760 auto& config = sensorConfig[inputSensorName];
761 archivedInputSensorNames.push_back(inputSensorName);
762 config.type = pidClass;
763 config.readPath =
764 getSensorPath(config.type, inputSensorName);
765 config.timeout = 0;
766 config.ignoreDbusMinMax = true;
767 config.unavailableAsFailed = unavailableAsFailed;
768 }
769 else
770 {
771 // When an input sensor is NOT on DBus, and it's in
772 // the MissingIsAcceptable list. Ignore it and
773 // continue with the next input sensor.
774 std::cout
775 << "Pid controller: Missing a missing-acceptable sensor from D-Bus "
776 << sensorName << "\n";
777 continue;
778 }
779 }
780#endif
Jason Lingf3b04fd2020-07-24 09:33:04 -0700781 }
782 for (const std::string& sensorName : outputSensorNames)
783 {
784 findSensors(sensors, sensorNameToDbusName(sensorName),
785 outputSensorInterfaces);
James Feist1738e2a2019-02-04 15:57:03 -0800786 }
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800787 for (const std::string& sensorName :
788 missingAcceptableSensorNames)
789 {
790 findSensors(sensors, sensorNameToDbusName(sensorName),
791 missingAcceptableSensorInterfaces);
792 }
James Feist50fdfe32018-09-24 15:51:09 -0700793
Jason Lingf3b04fd2020-07-24 09:33:04 -0700794 for (const SensorInterfaceType& inputSensorInterface :
795 inputSensorInterfaces)
James Feist1738e2a2019-02-04 15:57:03 -0800796 {
Jason Lingf3b04fd2020-07-24 09:33:04 -0700797 const std::string& dbusInterface =
798 inputSensorInterface.second;
799 const std::string& inputSensorPath =
800 inputSensorInterface.first;
Josh Lehanfb82a872020-09-20 21:48:22 -0700801
802 // Setting timeout to 0 is intentional, as D-Bus passive
803 // sensor updates are pushed in, not pulled by timer poll.
804 // Setting ignoreDbusMinMax is intentional, as this
805 // prevents normalization of values to [0.0, 1.0] range,
806 // which would mess up the PID loop math.
807 // All non-fan PID classes should be initialized this way.
808 // As for why a fan should not use this code path, see
809 // the ed1dafdf168def37c65bfb7a5efd18d9dbe04727 commit.
Josh Lehan23e22b92022-11-12 22:37:58 -0800810 if ((pidClass == "temp") || (pidClass == "margin") ||
811 (pidClass == "power") || (pidClass == "powersum"))
James Feist50fdfe32018-09-24 15:51:09 -0700812 {
Harvey.Wued1dafd2022-02-09 13:53:20 +0800813 std::string inputSensorName =
814 getSensorNameFromPath(inputSensorPath);
815 auto& config = sensorConfig[inputSensorName];
Chaul Lya552fe22024-11-15 10:20:28 +0000816 archivedInputSensorNames.push_back(inputSensorName);
Harvey.Wued1dafd2022-02-09 13:53:20 +0800817 config.type = pidClass;
818 config.readPath = inputSensorInterface.first;
Jason Lingf3b04fd2020-07-24 09:33:04 -0700819 config.timeout = 0;
820 config.ignoreDbusMinMax = true;
Alex.Song8f73ad72021-10-07 00:18:27 +0800821 config.unavailableAsFailed = unavailableAsFailed;
James Feist50fdfe32018-09-24 15:51:09 -0700822 }
Josh Lehanfb82a872020-09-20 21:48:22 -0700823
Jason Lingf3b04fd2020-07-24 09:33:04 -0700824 if (dbusInterface != sensorInterface)
James Feist50fdfe32018-09-24 15:51:09 -0700825 {
Jason Lingf3b04fd2020-07-24 09:33:04 -0700826 /* all expected inputs in the configuration are expected
827 * to be sensor interfaces
828 */
Ed Tanous7d6e2252025-06-27 10:45:25 -0700829 throw std::runtime_error(std::format(
830 "sensor at dbus path [{}] has an interface [{}] that does not match the expected interface of {}",
831 inputSensorPath, dbusInterface, sensorInterface));
James Feist50fdfe32018-09-24 15:51:09 -0700832 }
833 }
James Feist1738e2a2019-02-04 15:57:03 -0800834
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800835 // MissingIsAcceptable same postprocessing as Inputs
836 missingAcceptableSensorNames.clear();
837 for (const SensorInterfaceType&
838 missingAcceptableSensorInterface :
839 missingAcceptableSensorInterfaces)
840 {
841 const std::string& dbusInterface =
842 missingAcceptableSensorInterface.second;
843 const std::string& missingAcceptableSensorPath =
844 missingAcceptableSensorInterface.first;
845
846 std::string missingAcceptableSensorName =
847 getSensorNameFromPath(missingAcceptableSensorPath);
848 missingAcceptableSensorNames.push_back(
849 missingAcceptableSensorName);
850
851 if (dbusInterface != sensorInterface)
852 {
853 /* MissingIsAcceptable same error checking as Inputs
854 */
Ed Tanous7d6e2252025-06-27 10:45:25 -0700855 throw std::runtime_error(std::format(
856 "sensor at dbus path [{}] has an interface [{}] that does not match the expected interface of {}",
857 missingAcceptableSensorPath, dbusInterface,
858 sensorInterface));
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800859 }
860 }
861
Jason Lingf3b04fd2020-07-24 09:33:04 -0700862 /* fan pids need to pair up tach sensors with their pwm
863 * counterparts
864 */
865 if (pidClass == "fan")
866 {
867 /* If a PID is a fan there should be either
868 * (1) one output(pwm) per input(tach)
869 * OR
870 * (2) one putput(pwm) for all inputs(tach)
871 * everything else indicates a bad configuration.
872 */
873 bool singlePwm = false;
874 if (outputSensorInterfaces.size() == 1)
875 {
876 /* one pwm, set write paths for all fan sensors to it */
877 singlePwm = true;
878 }
879 else if (inputSensorInterfaces.size() ==
880 outputSensorInterfaces.size())
881 {
882 /* one to one mapping, each fan sensor gets its own pwm
883 * control */
884 singlePwm = false;
885 }
886 else
887 {
888 throw std::runtime_error(
889 "fan PID has invalid number of Outputs");
890 }
891 std::string fanSensorName;
892 std::string pwmPath;
893 std::string pwmInterface;
Harvey.Wued1dafd2022-02-09 13:53:20 +0800894 std::string pwmSensorName;
Jason Lingf3b04fd2020-07-24 09:33:04 -0700895 if (singlePwm)
896 {
897 /* if just a single output(pwm) is provided then use
898 * that pwm control path for all the fan sensor write
899 * path configs
900 */
901 pwmPath = outputSensorInterfaces.at(0).first;
902 pwmInterface = outputSensorInterfaces.at(0).second;
903 }
904 for (uint32_t idx = 0; idx < inputSensorInterfaces.size();
905 idx++)
906 {
907 if (!singlePwm)
908 {
909 pwmPath = outputSensorInterfaces.at(idx).first;
910 pwmInterface =
911 outputSensorInterfaces.at(idx).second;
912 }
Patrick Venture0911bfe2020-08-10 12:51:40 -0700913 if (defaultPwmInterface != pwmInterface)
Jason Lingf3b04fd2020-07-24 09:33:04 -0700914 {
Ed Tanous7d6e2252025-06-27 10:45:25 -0700915 throw std::runtime_error(std::format(
916 "fan pwm control at dbus path [{}] has an interface [{}] that does not match the expected interface of {}",
917 pwmPath, pwmInterface, defaultPwmInterface));
Jason Lingf3b04fd2020-07-24 09:33:04 -0700918 }
919 const std::string& fanPath =
920 inputSensorInterfaces.at(idx).first;
921 fanSensorName = getSensorNameFromPath(fanPath);
Harvey.Wued1dafd2022-02-09 13:53:20 +0800922 pwmSensorName = getSensorNameFromPath(pwmPath);
923 std::string fanPwmIndex = fanSensorName + pwmSensorName;
Chaul Lya552fe22024-11-15 10:20:28 +0000924 archivedInputSensorNames.push_back(fanPwmIndex);
Harvey.Wued1dafd2022-02-09 13:53:20 +0800925 auto& fanConfig = sensorConfig[fanPwmIndex];
926 fanConfig.type = pidClass;
927 fanConfig.readPath = fanPath;
Jason Lingf3b04fd2020-07-24 09:33:04 -0700928 fanConfig.writePath = pwmPath;
929 // todo: un-hardcode this if there are fans with
930 // different ranges
931 fanConfig.max = 255;
932 fanConfig.min = 0;
933 }
934 }
James Feist11d243d2019-06-24 16:18:40 -0700935 // if the sensors aren't available in the current state, don't
936 // add them to the configuration.
Chaul Lya552fe22024-11-15 10:20:28 +0000937 if (archivedInputSensorNames.empty())
James Feist11d243d2019-06-24 16:18:40 -0700938 {
939 continue;
940 }
941
James Feist5ec20272019-07-10 11:59:57 -0700942 std::string offsetType;
James Feist50fdfe32018-09-24 15:51:09 -0700943
James Feist5ec20272019-07-10 11:59:57 -0700944 // SetPointOffset is a threshold value to pull from the sensor
945 // to apply an offset. For upper thresholds this means the
946 // setpoint is usually negative.
947 auto findSetpointOffset = base.find("SetPointOffset");
948 if (findSetpointOffset != base.end())
James Feist22c257a2018-08-31 14:07:12 -0700949 {
James Feist5ec20272019-07-10 11:59:57 -0700950 offsetType =
951 std::get<std::string>(findSetpointOffset->second);
952 if (std::find(thresholds::types.begin(),
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400953 thresholds::types.end(), offsetType) ==
954 thresholds::types.end())
James Feist5ec20272019-07-10 11:59:57 -0700955 {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400956 throw std::runtime_error(
957 "Unsupported type: " + offsetType);
James Feist5ec20272019-07-10 11:59:57 -0700958 }
959 }
960
Josh Lehan31058fd2023-01-13 11:06:16 -0800961 std::vector<double> inputTempToMargin;
962
963 auto findTempToMargin = base.find("TempToMargin");
964 if (findTempToMargin != base.end())
965 {
966 inputTempToMargin =
967 std::get<std::vector<double>>(findTempToMargin->second);
968 }
969
970 std::vector<pid_control::conf::SensorInput> sensorInputs =
Chaul Lya552fe22024-11-15 10:20:28 +0000971 spliceInputs(archivedInputSensorNames, inputTempToMargin,
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800972 missingAcceptableSensorNames);
Josh Lehan31058fd2023-01-13 11:06:16 -0800973
James Feist5ec20272019-07-10 11:59:57 -0700974 if (offsetType.empty())
975 {
ykchiu7c6d35d2023-05-10 17:01:46 +0800976 conf::ControllerInfo& info = conf[pidName];
Josh Lehan31058fd2023-01-13 11:06:16 -0800977 info.inputs = std::move(sensorInputs);
Patrick Venture73823182020-10-08 15:12:51 -0700978 populatePidInfo(bus, base, info, nullptr, sensorConfig);
James Feist22c257a2018-08-31 14:07:12 -0700979 }
980 else
981 {
James Feist5ec20272019-07-10 11:59:57 -0700982 // we have to split up the inputs, as in practice t-control
983 // values will differ, making setpoints differ
Josh Lehan31058fd2023-01-13 11:06:16 -0800984 for (const pid_control::conf::SensorInput& input :
985 sensorInputs)
James Feist5ec20272019-07-10 11:59:57 -0700986 {
Josh Lehan31058fd2023-01-13 11:06:16 -0800987 conf::ControllerInfo& info = conf[input.name];
James Feist5ec20272019-07-10 11:59:57 -0700988 info.inputs.emplace_back(input);
Patrick Venture73823182020-10-08 15:12:51 -0700989 populatePidInfo(bus, base, info, &offsetType,
990 sensorConfig);
James Feist5ec20272019-07-10 11:59:57 -0700991 }
James Feist22c257a2018-08-31 14:07:12 -0700992 }
James Feist22c257a2018-08-31 14:07:12 -0700993 }
994 }
995 auto findStepwise =
996 configuration.second.find(stepwiseConfigurationInterface);
997 if (findStepwise != configuration.second.end())
998 {
999 const auto& base = findStepwise->second;
ykchiu7c6d35d2023-05-10 17:01:46 +08001000 const std::string pidName =
1001 sensorNameToDbusName(std::get<std::string>(base.at("Name")));
James Feist22c257a2018-08-31 14:07:12 -07001002 const std::vector<std::string>& zones =
James Feist1f802f52019-02-08 13:51:43 -08001003 std::get<std::vector<std::string>>(base.at("Zones"));
James Feist22c257a2018-08-31 14:07:12 -07001004 for (const std::string& zone : zones)
1005 {
Josh Lehan998fbe62020-09-20 21:21:05 -07001006 auto index = getZoneIndex(zone, foundZones);
1007
James Feistf81f2882019-02-26 11:26:36 -08001008 conf::PIDConf& conf = zoneConfig[index];
James Feist50fdfe32018-09-24 15:51:09 -07001009
1010 std::vector<std::string> inputs;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001011 std::vector<std::string> missingAcceptableSensors;
1012 std::vector<std::string> missingAcceptableSensorNames;
James Feist50fdfe32018-09-24 15:51:09 -07001013 std::vector<std::string> sensorNames =
James Feist1f802f52019-02-08 13:51:43 -08001014 std::get<std::vector<std::string>>(base.at("Inputs"));
James Feist50fdfe32018-09-24 15:51:09 -07001015
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001016 auto findMissingAcceptable = base.find("MissingIsAcceptable");
1017 if (findMissingAcceptable != base.end())
1018 {
1019 missingAcceptableSensorNames =
1020 std::get<std::vector<std::string>>(
1021 findMissingAcceptable->second);
1022 }
1023
Alex.Song8f73ad72021-10-07 00:18:27 +08001024 bool unavailableAsFailed = true;
1025 auto findUnavailableAsFailed =
1026 base.find("InputUnavailableAsFailed");
1027 if (findUnavailableAsFailed != base.end())
1028 {
1029 unavailableAsFailed =
1030 std::get<bool>(findUnavailableAsFailed->second);
1031 }
1032
James Feist1738e2a2019-02-04 15:57:03 -08001033 bool sensorFound = false;
James Feist50fdfe32018-09-24 15:51:09 -07001034 for (const std::string& sensorName : sensorNames)
1035 {
James Feist1738e2a2019-02-04 15:57:03 -08001036 std::vector<std::pair<std::string, std::string>>
1037 sensorPathIfacePairs;
Jason Lingf3b04fd2020-07-24 09:33:04 -07001038 if (!findSensors(sensors, sensorNameToDbusName(sensorName),
1039 sensorPathIfacePairs))
James Feist50fdfe32018-09-24 15:51:09 -07001040 {
Chaul Lya552fe22024-11-15 10:20:28 +00001041#ifndef HANDLE_MISSING_OBJECT_PATHS
James Feist50fdfe32018-09-24 15:51:09 -07001042 break;
Chaul Lya552fe22024-11-15 10:20:28 +00001043#else
1044 if (std::find(missingAcceptableSensorNames.begin(),
1045 missingAcceptableSensorNames.end(),
1046 sensorName) ==
1047 missingAcceptableSensorNames.end())
1048 {
1049 // When an input sensor is NOT on DBus, and it's NOT
1050 // in the MissingIsAcceptable list. Build it as a
1051 // failed sensor with default information (temp
1052 // sensor path, temp type, ...)
1053 std::cerr
1054 << "Stepwise controller: Missing a missing-unacceptable sensor from D-Bus "
1055 << sensorName << "\n";
1056 std::string shortName =
1057 sensorNameToDbusName(sensorName);
1058
1059 inputs.push_back(shortName);
1060 auto& config = sensorConfig[shortName];
1061 config.type = "temp";
1062 config.readPath =
1063 getSensorPath(config.type, shortName);
1064 config.ignoreDbusMinMax = true;
1065 config.unavailableAsFailed = unavailableAsFailed;
1066 // todo: maybe un-hardcode this if we run into
1067 // slower timeouts with sensors
1068
1069 config.timeout = 0;
1070 sensorFound = true;
1071 }
1072 else
1073 {
1074 // When an input sensor is NOT on DBus, and it's in
1075 // the MissingIsAcceptable list. Ignore it and
1076 // continue with the next input sensor.
1077 std::cout
1078 << "Stepwise controller: Missing a missing-acceptable sensor from D-Bus "
1079 << sensorName << "\n";
1080 continue;
1081 }
1082#endif
James Feist50fdfe32018-09-24 15:51:09 -07001083 }
Chaul Lya552fe22024-11-15 10:20:28 +00001084 else
James Feist1738e2a2019-02-04 15:57:03 -08001085 {
Chaul Lya552fe22024-11-15 10:20:28 +00001086 for (const auto& sensorPathIfacePair :
1087 sensorPathIfacePairs)
1088 {
1089 std::string shortName = getSensorNameFromPath(
1090 sensorPathIfacePair.first);
James Feist50fdfe32018-09-24 15:51:09 -07001091
Chaul Lya552fe22024-11-15 10:20:28 +00001092 inputs.push_back(shortName);
1093 auto& config = sensorConfig[shortName];
1094 config.readPath = sensorPathIfacePair.first;
1095 config.type = "temp";
1096 config.ignoreDbusMinMax = true;
1097 config.unavailableAsFailed = unavailableAsFailed;
1098 // todo: maybe un-hardcode this if we run into
1099 // slower timeouts with sensors
James Feist1738e2a2019-02-04 15:57:03 -08001100
Chaul Lya552fe22024-11-15 10:20:28 +00001101 config.timeout = 0;
1102 sensorFound = true;
1103 }
James Feist1738e2a2019-02-04 15:57:03 -08001104 }
James Feist50fdfe32018-09-24 15:51:09 -07001105 }
1106 if (!sensorFound)
1107 {
1108 continue;
1109 }
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001110
1111 // MissingIsAcceptable same postprocessing as Inputs
1112 for (const std::string& missingAcceptableSensorName :
1113 missingAcceptableSensorNames)
1114 {
1115 std::vector<std::pair<std::string, std::string>>
1116 sensorPathIfacePairs;
1117 if (!findSensors(
1118 sensors,
1119 sensorNameToDbusName(missingAcceptableSensorName),
1120 sensorPathIfacePairs))
1121 {
Chaul Lya552fe22024-11-15 10:20:28 +00001122#ifndef HANDLE_MISSING_OBJECT_PATHS
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001123 break;
Chaul Lya552fe22024-11-15 10:20:28 +00001124#else
1125 // When a sensor in the MissingIsAcceptable list is NOT
1126 // on DBus and it still reaches here, which contradicts
1127 // to what we did in the Input sensor building step.
1128 // Continue.
1129 continue;
1130#endif
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001131 }
1132
1133 for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
1134 {
1135 std::string shortName =
1136 getSensorNameFromPath(sensorPathIfacePair.first);
1137
1138 missingAcceptableSensors.push_back(shortName);
1139 }
1140 }
1141
ykchiu7c6d35d2023-05-10 17:01:46 +08001142 conf::ControllerInfo& info = conf[pidName];
Josh Lehan31058fd2023-01-13 11:06:16 -08001143
1144 std::vector<double> inputTempToMargin;
1145
1146 auto findTempToMargin = base.find("TempToMargin");
1147 if (findTempToMargin != base.end())
1148 {
1149 inputTempToMargin =
1150 std::get<std::vector<double>>(findTempToMargin->second);
1151 }
1152
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001153 info.inputs = spliceInputs(inputs, inputTempToMargin,
1154 missingAcceptableSensors);
James Feist50fdfe32018-09-24 15:51:09 -07001155
James Feist22c257a2018-08-31 14:07:12 -07001156 info.type = "stepwise";
1157 info.stepwiseInfo.ts = 1.0; // currently unused
James Feist3dfaafd2018-09-20 15:46:58 -07001158 info.stepwiseInfo.positiveHysteresis = 0.0;
1159 info.stepwiseInfo.negativeHysteresis = 0.0;
James Feist608304d2019-02-25 10:01:42 -08001160 std::string subtype = std::get<std::string>(base.at("Class"));
1161
1162 info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
James Feist3dfaafd2018-09-20 15:46:58 -07001163 auto findPosHyst = base.find("PositiveHysteresis");
1164 auto findNegHyst = base.find("NegativeHysteresis");
1165 if (findPosHyst != base.end())
1166 {
James Feist1f802f52019-02-08 13:51:43 -08001167 info.stepwiseInfo.positiveHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -08001168 VariantToDoubleVisitor(), findPosHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -07001169 }
1170 if (findNegHyst != base.end())
1171 {
James Feist5782ab82019-04-02 08:38:48 -07001172 info.stepwiseInfo.negativeHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -08001173 VariantToDoubleVisitor(), findNegHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -07001174 }
James Feist22c257a2018-08-31 14:07:12 -07001175 std::vector<double> readings =
James Feist1f802f52019-02-08 13:51:43 -08001176 std::get<std::vector<double>>(base.at("Reading"));
James Feist22c257a2018-08-31 14:07:12 -07001177 if (readings.size() > ec::maxStepwisePoints)
1178 {
1179 throw std::invalid_argument("Too many stepwise points.");
1180 }
1181 if (readings.empty())
1182 {
1183 throw std::invalid_argument(
1184 "Must have one stepwise point.");
1185 }
1186 std::copy(readings.begin(), readings.end(),
1187 info.stepwiseInfo.reading);
1188 if (readings.size() < ec::maxStepwisePoints)
1189 {
1190 info.stepwiseInfo.reading[readings.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -08001191 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -07001192 }
1193 std::vector<double> outputs =
James Feist1f802f52019-02-08 13:51:43 -08001194 std::get<std::vector<double>>(base.at("Output"));
James Feist22c257a2018-08-31 14:07:12 -07001195 if (readings.size() != outputs.size())
1196 {
1197 throw std::invalid_argument(
1198 "Outputs size must match readings");
1199 }
1200 std::copy(outputs.begin(), outputs.end(),
1201 info.stepwiseInfo.output);
1202 if (outputs.size() < ec::maxStepwisePoints)
1203 {
1204 info.stepwiseInfo.output[outputs.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -08001205 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -07001206 }
James Feist7136a5a2018-07-19 09:52:05 -07001207 }
1208 }
1209 }
Patrick Venture39199b42020-10-08 14:40:29 -07001210 if constexpr (pid_control::conf::DEBUG)
James Feist7136a5a2018-07-19 09:52:05 -07001211 {
Patrick Venture39199b42020-10-08 14:40:29 -07001212 debugPrint(sensorConfig, zoneConfig, zoneDetailsConfig);
James Feist7136a5a2018-07-19 09:52:05 -07001213 }
James Feistc959c422018-11-01 12:33:40 -07001214 if (zoneConfig.empty() || zoneDetailsConfig.empty())
James Feist50fdfe32018-09-24 15:51:09 -07001215 {
James Feist1fe08952019-05-07 09:17:16 -07001216 std::cerr
1217 << "No fan zones, application pausing until new configuration\n";
1218 return false;
James Feist50fdfe32018-09-24 15:51:09 -07001219 }
James Feist1fe08952019-05-07 09:17:16 -07001220 return true;
James Feist7136a5a2018-07-19 09:52:05 -07001221}
Patrick Venturea0764872020-08-08 07:48:43 -07001222
James Feist7136a5a2018-07-19 09:52:05 -07001223} // namespace dbus_configuration
Patrick Venturea0764872020-08-08 07:48:43 -07001224} // namespace pid_control