blob: 7e34b559b605597a57a340734fe39f033ce805be [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{
76 return dbusPath.substr(dbusPath.find_last_of("/") + 1);
77}
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 */
829 throw std::runtime_error(
830 "sensor at dbus path [" + inputSensorPath +
831 "] has an interface [" + dbusInterface +
832 "] that does not match the expected interface of " +
833 sensorInterface);
James Feist50fdfe32018-09-24 15:51:09 -0700834 }
835 }
James Feist1738e2a2019-02-04 15:57:03 -0800836
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800837 // MissingIsAcceptable same postprocessing as Inputs
838 missingAcceptableSensorNames.clear();
839 for (const SensorInterfaceType&
840 missingAcceptableSensorInterface :
841 missingAcceptableSensorInterfaces)
842 {
843 const std::string& dbusInterface =
844 missingAcceptableSensorInterface.second;
845 const std::string& missingAcceptableSensorPath =
846 missingAcceptableSensorInterface.first;
847
848 std::string missingAcceptableSensorName =
849 getSensorNameFromPath(missingAcceptableSensorPath);
850 missingAcceptableSensorNames.push_back(
851 missingAcceptableSensorName);
852
853 if (dbusInterface != sensorInterface)
854 {
855 /* MissingIsAcceptable same error checking as Inputs
856 */
857 throw std::runtime_error(
858 "sensor at dbus path [" +
859 missingAcceptableSensorPath +
860 "] has an interface [" + dbusInterface +
861 "] that does not match the expected interface of " +
862 sensorInterface);
863 }
864 }
865
Jason Lingf3b04fd2020-07-24 09:33:04 -0700866 /* fan pids need to pair up tach sensors with their pwm
867 * counterparts
868 */
869 if (pidClass == "fan")
870 {
871 /* If a PID is a fan there should be either
872 * (1) one output(pwm) per input(tach)
873 * OR
874 * (2) one putput(pwm) for all inputs(tach)
875 * everything else indicates a bad configuration.
876 */
877 bool singlePwm = false;
878 if (outputSensorInterfaces.size() == 1)
879 {
880 /* one pwm, set write paths for all fan sensors to it */
881 singlePwm = true;
882 }
883 else if (inputSensorInterfaces.size() ==
884 outputSensorInterfaces.size())
885 {
886 /* one to one mapping, each fan sensor gets its own pwm
887 * control */
888 singlePwm = false;
889 }
890 else
891 {
892 throw std::runtime_error(
893 "fan PID has invalid number of Outputs");
894 }
895 std::string fanSensorName;
896 std::string pwmPath;
897 std::string pwmInterface;
Harvey.Wued1dafd2022-02-09 13:53:20 +0800898 std::string pwmSensorName;
Jason Lingf3b04fd2020-07-24 09:33:04 -0700899 if (singlePwm)
900 {
901 /* if just a single output(pwm) is provided then use
902 * that pwm control path for all the fan sensor write
903 * path configs
904 */
905 pwmPath = outputSensorInterfaces.at(0).first;
906 pwmInterface = outputSensorInterfaces.at(0).second;
907 }
908 for (uint32_t idx = 0; idx < inputSensorInterfaces.size();
909 idx++)
910 {
911 if (!singlePwm)
912 {
913 pwmPath = outputSensorInterfaces.at(idx).first;
914 pwmInterface =
915 outputSensorInterfaces.at(idx).second;
916 }
Patrick Venture0911bfe2020-08-10 12:51:40 -0700917 if (defaultPwmInterface != pwmInterface)
Jason Lingf3b04fd2020-07-24 09:33:04 -0700918 {
919 throw std::runtime_error(
920 "fan pwm control at dbus path [" + pwmPath +
921 "] has an interface [" + pwmInterface +
922 "] that does not match the expected interface "
923 "of " +
Patrick Venture0911bfe2020-08-10 12:51:40 -0700924 defaultPwmInterface);
Jason Lingf3b04fd2020-07-24 09:33:04 -0700925 }
926 const std::string& fanPath =
927 inputSensorInterfaces.at(idx).first;
928 fanSensorName = getSensorNameFromPath(fanPath);
Harvey.Wued1dafd2022-02-09 13:53:20 +0800929 pwmSensorName = getSensorNameFromPath(pwmPath);
930 std::string fanPwmIndex = fanSensorName + pwmSensorName;
Chaul Lya552fe22024-11-15 10:20:28 +0000931 archivedInputSensorNames.push_back(fanPwmIndex);
Harvey.Wued1dafd2022-02-09 13:53:20 +0800932 auto& fanConfig = sensorConfig[fanPwmIndex];
933 fanConfig.type = pidClass;
934 fanConfig.readPath = fanPath;
Jason Lingf3b04fd2020-07-24 09:33:04 -0700935 fanConfig.writePath = pwmPath;
936 // todo: un-hardcode this if there are fans with
937 // different ranges
938 fanConfig.max = 255;
939 fanConfig.min = 0;
940 }
941 }
James Feist11d243d2019-06-24 16:18:40 -0700942 // if the sensors aren't available in the current state, don't
943 // add them to the configuration.
Chaul Lya552fe22024-11-15 10:20:28 +0000944 if (archivedInputSensorNames.empty())
James Feist11d243d2019-06-24 16:18:40 -0700945 {
946 continue;
947 }
948
James Feist5ec20272019-07-10 11:59:57 -0700949 std::string offsetType;
James Feist50fdfe32018-09-24 15:51:09 -0700950
James Feist5ec20272019-07-10 11:59:57 -0700951 // SetPointOffset is a threshold value to pull from the sensor
952 // to apply an offset. For upper thresholds this means the
953 // setpoint is usually negative.
954 auto findSetpointOffset = base.find("SetPointOffset");
955 if (findSetpointOffset != base.end())
James Feist22c257a2018-08-31 14:07:12 -0700956 {
James Feist5ec20272019-07-10 11:59:57 -0700957 offsetType =
958 std::get<std::string>(findSetpointOffset->second);
959 if (std::find(thresholds::types.begin(),
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400960 thresholds::types.end(), offsetType) ==
961 thresholds::types.end())
James Feist5ec20272019-07-10 11:59:57 -0700962 {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400963 throw std::runtime_error(
964 "Unsupported type: " + offsetType);
James Feist5ec20272019-07-10 11:59:57 -0700965 }
966 }
967
Josh Lehan31058fd2023-01-13 11:06:16 -0800968 std::vector<double> inputTempToMargin;
969
970 auto findTempToMargin = base.find("TempToMargin");
971 if (findTempToMargin != base.end())
972 {
973 inputTempToMargin =
974 std::get<std::vector<double>>(findTempToMargin->second);
975 }
976
977 std::vector<pid_control::conf::SensorInput> sensorInputs =
Chaul Lya552fe22024-11-15 10:20:28 +0000978 spliceInputs(archivedInputSensorNames, inputTempToMargin,
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800979 missingAcceptableSensorNames);
Josh Lehan31058fd2023-01-13 11:06:16 -0800980
James Feist5ec20272019-07-10 11:59:57 -0700981 if (offsetType.empty())
982 {
ykchiu7c6d35d2023-05-10 17:01:46 +0800983 conf::ControllerInfo& info = conf[pidName];
Josh Lehan31058fd2023-01-13 11:06:16 -0800984 info.inputs = std::move(sensorInputs);
Patrick Venture73823182020-10-08 15:12:51 -0700985 populatePidInfo(bus, base, info, nullptr, sensorConfig);
James Feist22c257a2018-08-31 14:07:12 -0700986 }
987 else
988 {
James Feist5ec20272019-07-10 11:59:57 -0700989 // we have to split up the inputs, as in practice t-control
990 // values will differ, making setpoints differ
Josh Lehan31058fd2023-01-13 11:06:16 -0800991 for (const pid_control::conf::SensorInput& input :
992 sensorInputs)
James Feist5ec20272019-07-10 11:59:57 -0700993 {
Josh Lehan31058fd2023-01-13 11:06:16 -0800994 conf::ControllerInfo& info = conf[input.name];
James Feist5ec20272019-07-10 11:59:57 -0700995 info.inputs.emplace_back(input);
Patrick Venture73823182020-10-08 15:12:51 -0700996 populatePidInfo(bus, base, info, &offsetType,
997 sensorConfig);
James Feist5ec20272019-07-10 11:59:57 -0700998 }
James Feist22c257a2018-08-31 14:07:12 -0700999 }
James Feist22c257a2018-08-31 14:07:12 -07001000 }
1001 }
1002 auto findStepwise =
1003 configuration.second.find(stepwiseConfigurationInterface);
1004 if (findStepwise != configuration.second.end())
1005 {
1006 const auto& base = findStepwise->second;
ykchiu7c6d35d2023-05-10 17:01:46 +08001007 const std::string pidName =
1008 sensorNameToDbusName(std::get<std::string>(base.at("Name")));
James Feist22c257a2018-08-31 14:07:12 -07001009 const std::vector<std::string>& zones =
James Feist1f802f52019-02-08 13:51:43 -08001010 std::get<std::vector<std::string>>(base.at("Zones"));
James Feist22c257a2018-08-31 14:07:12 -07001011 for (const std::string& zone : zones)
1012 {
Josh Lehan998fbe62020-09-20 21:21:05 -07001013 auto index = getZoneIndex(zone, foundZones);
1014
James Feistf81f2882019-02-26 11:26:36 -08001015 conf::PIDConf& conf = zoneConfig[index];
James Feist50fdfe32018-09-24 15:51:09 -07001016
1017 std::vector<std::string> inputs;
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001018 std::vector<std::string> missingAcceptableSensors;
1019 std::vector<std::string> missingAcceptableSensorNames;
James Feist50fdfe32018-09-24 15:51:09 -07001020 std::vector<std::string> sensorNames =
James Feist1f802f52019-02-08 13:51:43 -08001021 std::get<std::vector<std::string>>(base.at("Inputs"));
James Feist50fdfe32018-09-24 15:51:09 -07001022
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001023 auto findMissingAcceptable = base.find("MissingIsAcceptable");
1024 if (findMissingAcceptable != base.end())
1025 {
1026 missingAcceptableSensorNames =
1027 std::get<std::vector<std::string>>(
1028 findMissingAcceptable->second);
1029 }
1030
Alex.Song8f73ad72021-10-07 00:18:27 +08001031 bool unavailableAsFailed = true;
1032 auto findUnavailableAsFailed =
1033 base.find("InputUnavailableAsFailed");
1034 if (findUnavailableAsFailed != base.end())
1035 {
1036 unavailableAsFailed =
1037 std::get<bool>(findUnavailableAsFailed->second);
1038 }
1039
James Feist1738e2a2019-02-04 15:57:03 -08001040 bool sensorFound = false;
James Feist50fdfe32018-09-24 15:51:09 -07001041 for (const std::string& sensorName : sensorNames)
1042 {
James Feist1738e2a2019-02-04 15:57:03 -08001043 std::vector<std::pair<std::string, std::string>>
1044 sensorPathIfacePairs;
Jason Lingf3b04fd2020-07-24 09:33:04 -07001045 if (!findSensors(sensors, sensorNameToDbusName(sensorName),
1046 sensorPathIfacePairs))
James Feist50fdfe32018-09-24 15:51:09 -07001047 {
Chaul Lya552fe22024-11-15 10:20:28 +00001048#ifndef HANDLE_MISSING_OBJECT_PATHS
James Feist50fdfe32018-09-24 15:51:09 -07001049 break;
Chaul Lya552fe22024-11-15 10:20:28 +00001050#else
1051 if (std::find(missingAcceptableSensorNames.begin(),
1052 missingAcceptableSensorNames.end(),
1053 sensorName) ==
1054 missingAcceptableSensorNames.end())
1055 {
1056 // When an input sensor is NOT on DBus, and it's NOT
1057 // in the MissingIsAcceptable list. Build it as a
1058 // failed sensor with default information (temp
1059 // sensor path, temp type, ...)
1060 std::cerr
1061 << "Stepwise controller: Missing a missing-unacceptable sensor from D-Bus "
1062 << sensorName << "\n";
1063 std::string shortName =
1064 sensorNameToDbusName(sensorName);
1065
1066 inputs.push_back(shortName);
1067 auto& config = sensorConfig[shortName];
1068 config.type = "temp";
1069 config.readPath =
1070 getSensorPath(config.type, shortName);
1071 config.ignoreDbusMinMax = true;
1072 config.unavailableAsFailed = unavailableAsFailed;
1073 // todo: maybe un-hardcode this if we run into
1074 // slower timeouts with sensors
1075
1076 config.timeout = 0;
1077 sensorFound = true;
1078 }
1079 else
1080 {
1081 // When an input sensor is NOT on DBus, and it's in
1082 // the MissingIsAcceptable list. Ignore it and
1083 // continue with the next input sensor.
1084 std::cout
1085 << "Stepwise controller: Missing a missing-acceptable sensor from D-Bus "
1086 << sensorName << "\n";
1087 continue;
1088 }
1089#endif
James Feist50fdfe32018-09-24 15:51:09 -07001090 }
Chaul Lya552fe22024-11-15 10:20:28 +00001091 else
James Feist1738e2a2019-02-04 15:57:03 -08001092 {
Chaul Lya552fe22024-11-15 10:20:28 +00001093 for (const auto& sensorPathIfacePair :
1094 sensorPathIfacePairs)
1095 {
1096 std::string shortName = getSensorNameFromPath(
1097 sensorPathIfacePair.first);
James Feist50fdfe32018-09-24 15:51:09 -07001098
Chaul Lya552fe22024-11-15 10:20:28 +00001099 inputs.push_back(shortName);
1100 auto& config = sensorConfig[shortName];
1101 config.readPath = sensorPathIfacePair.first;
1102 config.type = "temp";
1103 config.ignoreDbusMinMax = true;
1104 config.unavailableAsFailed = unavailableAsFailed;
1105 // todo: maybe un-hardcode this if we run into
1106 // slower timeouts with sensors
James Feist1738e2a2019-02-04 15:57:03 -08001107
Chaul Lya552fe22024-11-15 10:20:28 +00001108 config.timeout = 0;
1109 sensorFound = true;
1110 }
James Feist1738e2a2019-02-04 15:57:03 -08001111 }
James Feist50fdfe32018-09-24 15:51:09 -07001112 }
1113 if (!sensorFound)
1114 {
1115 continue;
1116 }
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001117
1118 // MissingIsAcceptable same postprocessing as Inputs
1119 for (const std::string& missingAcceptableSensorName :
1120 missingAcceptableSensorNames)
1121 {
1122 std::vector<std::pair<std::string, std::string>>
1123 sensorPathIfacePairs;
1124 if (!findSensors(
1125 sensors,
1126 sensorNameToDbusName(missingAcceptableSensorName),
1127 sensorPathIfacePairs))
1128 {
Chaul Lya552fe22024-11-15 10:20:28 +00001129#ifndef HANDLE_MISSING_OBJECT_PATHS
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001130 break;
Chaul Lya552fe22024-11-15 10:20:28 +00001131#else
1132 // When a sensor in the MissingIsAcceptable list is NOT
1133 // on DBus and it still reaches here, which contradicts
1134 // to what we did in the Input sensor building step.
1135 // Continue.
1136 continue;
1137#endif
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001138 }
1139
1140 for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
1141 {
1142 std::string shortName =
1143 getSensorNameFromPath(sensorPathIfacePair.first);
1144
1145 missingAcceptableSensors.push_back(shortName);
1146 }
1147 }
1148
ykchiu7c6d35d2023-05-10 17:01:46 +08001149 conf::ControllerInfo& info = conf[pidName];
Josh Lehan31058fd2023-01-13 11:06:16 -08001150
1151 std::vector<double> inputTempToMargin;
1152
1153 auto findTempToMargin = base.find("TempToMargin");
1154 if (findTempToMargin != base.end())
1155 {
1156 inputTempToMargin =
1157 std::get<std::vector<double>>(findTempToMargin->second);
1158 }
1159
Josh Lehan3f0f7bc2023-02-13 01:45:29 -08001160 info.inputs = spliceInputs(inputs, inputTempToMargin,
1161 missingAcceptableSensors);
James Feist50fdfe32018-09-24 15:51:09 -07001162
James Feist22c257a2018-08-31 14:07:12 -07001163 info.type = "stepwise";
1164 info.stepwiseInfo.ts = 1.0; // currently unused
James Feist3dfaafd2018-09-20 15:46:58 -07001165 info.stepwiseInfo.positiveHysteresis = 0.0;
1166 info.stepwiseInfo.negativeHysteresis = 0.0;
James Feist608304d2019-02-25 10:01:42 -08001167 std::string subtype = std::get<std::string>(base.at("Class"));
1168
1169 info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
James Feist3dfaafd2018-09-20 15:46:58 -07001170 auto findPosHyst = base.find("PositiveHysteresis");
1171 auto findNegHyst = base.find("NegativeHysteresis");
1172 if (findPosHyst != base.end())
1173 {
James Feist1f802f52019-02-08 13:51:43 -08001174 info.stepwiseInfo.positiveHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -08001175 VariantToDoubleVisitor(), findPosHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -07001176 }
1177 if (findNegHyst != base.end())
1178 {
James Feist5782ab82019-04-02 08:38:48 -07001179 info.stepwiseInfo.negativeHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -08001180 VariantToDoubleVisitor(), findNegHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -07001181 }
James Feist22c257a2018-08-31 14:07:12 -07001182 std::vector<double> readings =
James Feist1f802f52019-02-08 13:51:43 -08001183 std::get<std::vector<double>>(base.at("Reading"));
James Feist22c257a2018-08-31 14:07:12 -07001184 if (readings.size() > ec::maxStepwisePoints)
1185 {
1186 throw std::invalid_argument("Too many stepwise points.");
1187 }
1188 if (readings.empty())
1189 {
1190 throw std::invalid_argument(
1191 "Must have one stepwise point.");
1192 }
1193 std::copy(readings.begin(), readings.end(),
1194 info.stepwiseInfo.reading);
1195 if (readings.size() < ec::maxStepwisePoints)
1196 {
1197 info.stepwiseInfo.reading[readings.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -08001198 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -07001199 }
1200 std::vector<double> outputs =
James Feist1f802f52019-02-08 13:51:43 -08001201 std::get<std::vector<double>>(base.at("Output"));
James Feist22c257a2018-08-31 14:07:12 -07001202 if (readings.size() != outputs.size())
1203 {
1204 throw std::invalid_argument(
1205 "Outputs size must match readings");
1206 }
1207 std::copy(outputs.begin(), outputs.end(),
1208 info.stepwiseInfo.output);
1209 if (outputs.size() < ec::maxStepwisePoints)
1210 {
1211 info.stepwiseInfo.output[outputs.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -08001212 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -07001213 }
James Feist7136a5a2018-07-19 09:52:05 -07001214 }
1215 }
1216 }
Patrick Venture39199b42020-10-08 14:40:29 -07001217 if constexpr (pid_control::conf::DEBUG)
James Feist7136a5a2018-07-19 09:52:05 -07001218 {
Patrick Venture39199b42020-10-08 14:40:29 -07001219 debugPrint(sensorConfig, zoneConfig, zoneDetailsConfig);
James Feist7136a5a2018-07-19 09:52:05 -07001220 }
James Feistc959c422018-11-01 12:33:40 -07001221 if (zoneConfig.empty() || zoneDetailsConfig.empty())
James Feist50fdfe32018-09-24 15:51:09 -07001222 {
James Feist1fe08952019-05-07 09:17:16 -07001223 std::cerr
1224 << "No fan zones, application pausing until new configuration\n";
1225 return false;
James Feist50fdfe32018-09-24 15:51:09 -07001226 }
James Feist1fe08952019-05-07 09:17:16 -07001227 return true;
James Feist7136a5a2018-07-19 09:52:05 -07001228}
Patrick Venturea0764872020-08-08 07:48:43 -07001229
James Feist7136a5a2018-07-19 09:52:05 -07001230} // namespace dbus_configuration
Patrick Venturea0764872020-08-08 07:48:43 -07001231} // namespace pid_control