blob: bfa2a7cba4ceb42b1841f4a9d05e1721e789140d [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*/
16
Patrick Venture07716592018-10-14 11:46:40 -070017#include "conf.hpp"
James Feist0c8223b2019-05-08 15:33:33 -070018#include "util.hpp"
Patrick Venture07716592018-10-14 11:46:40 -070019
Patrick Venture107a25d2018-10-13 14:08:09 -070020#include <algorithm>
James Feist1fe08952019-05-07 09:17:16 -070021#include <boost/asio/steady_timer.hpp>
James Feist64f072a2018-08-10 16:39:24 -070022#include <chrono>
James Feist64f072a2018-08-10 16:39:24 -070023#include <functional>
James Feist7136a5a2018-07-19 09:52:05 -070024#include <iostream>
James Feist1fe08952019-05-07 09:17:16 -070025#include <list>
James Feist1738e2a2019-02-04 15:57:03 -080026#include <regex>
James Feist7136a5a2018-07-19 09:52:05 -070027#include <sdbusplus/bus.hpp>
James Feist64f072a2018-08-10 16:39:24 -070028#include <sdbusplus/bus/match.hpp>
James Feist22c257a2018-08-31 14:07:12 -070029#include <sdbusplus/exception.hpp>
James Feist7136a5a2018-07-19 09:52:05 -070030#include <set>
31#include <unordered_map>
James Feist1f802f52019-02-08 13:51:43 -080032#include <variant>
James Feist7136a5a2018-07-19 09:52:05 -070033
34static constexpr bool DEBUG = false; // enable to print found configuration
35
James Feistf81f2882019-02-26 11:26:36 -080036extern std::map<std::string, struct conf::SensorConfig> sensorConfig;
37extern std::map<int64_t, conf::PIDConf> zoneConfig;
38extern std::map<int64_t, struct conf::ZoneConfig> zoneDetailsConfig;
James Feist7136a5a2018-07-19 09:52:05 -070039
Patrick Venturee2ec0f62018-09-04 12:30:27 -070040constexpr const char* pidConfigurationInterface =
James Feist7136a5a2018-07-19 09:52:05 -070041 "xyz.openbmc_project.Configuration.Pid";
Patrick Venturee2ec0f62018-09-04 12:30:27 -070042constexpr const char* objectManagerInterface =
James Feist7136a5a2018-07-19 09:52:05 -070043 "org.freedesktop.DBus.ObjectManager";
Patrick Venturee2ec0f62018-09-04 12:30:27 -070044constexpr const char* pidZoneConfigurationInterface =
James Feist7136a5a2018-07-19 09:52:05 -070045 "xyz.openbmc_project.Configuration.Pid.Zone";
James Feist22c257a2018-08-31 14:07:12 -070046constexpr const char* stepwiseConfigurationInterface =
47 "xyz.openbmc_project.Configuration.Stepwise";
James Feistf0096a02019-02-21 11:25:22 -080048constexpr const char* thermalControlIface =
49 "xyz.openbmc_project.Control.ThermalMode";
Patrick Venturee2ec0f62018-09-04 12:30:27 -070050constexpr const char* sensorInterface = "xyz.openbmc_project.Sensor.Value";
51constexpr const char* pwmInterface = "xyz.openbmc_project.Control.FanPwm";
James Feist7136a5a2018-07-19 09:52:05 -070052
James Feist991ebd82020-07-21 11:14:52 -070053using Association = std::tuple<std::string, std::string, std::string>;
54using Associations = std::vector<Association>;
55
James Feist5ec20272019-07-10 11:59:57 -070056namespace thresholds
57{
58constexpr const char* warningInterface =
59 "xyz.openbmc_project.Sensor.Threshold.Warning";
60constexpr const char* criticalInterface =
61 "xyz.openbmc_project.Sensor.Threshold.Critical";
62const std::array<const char*, 4> types = {"CriticalLow", "CriticalHigh",
63 "WarningLow", "WarningHigh"};
64
65} // namespace thresholds
66
James Feist7136a5a2018-07-19 09:52:05 -070067namespace dbus_configuration
68{
69
James Feist5ec20272019-07-10 11:59:57 -070070using DbusVariantType =
71 std::variant<uint64_t, int64_t, double, std::string,
72 std::vector<std::string>, std::vector<double>>;
73
James Feist1738e2a2019-02-04 15:57:03 -080074bool findSensors(const std::unordered_map<std::string, std::string>& sensors,
75 const std::string& search,
76 std::vector<std::pair<std::string, std::string>>& matches)
James Feist7136a5a2018-07-19 09:52:05 -070077{
James Feist1738e2a2019-02-04 15:57:03 -080078 std::smatch match;
Jae Hyun Yoo1a704dc2020-06-04 15:00:10 -070079 std::regex reg(search + '$');
James Feist1738e2a2019-02-04 15:57:03 -080080 for (const auto& sensor : sensors)
James Feist7136a5a2018-07-19 09:52:05 -070081 {
James Feist1738e2a2019-02-04 15:57:03 -080082 if (std::regex_search(sensor.first, match, reg))
83 {
84 matches.push_back(sensor);
85 }
James Feist7136a5a2018-07-19 09:52:05 -070086 }
James Feist1738e2a2019-02-04 15:57:03 -080087 return matches.size() > 0;
James Feist7136a5a2018-07-19 09:52:05 -070088}
89
90// this function prints the configuration into a form similar to the cpp
91// generated code to help in verification, should be turned off during normal
92// use
93void debugPrint(void)
94{
95 // print sensor config
96 std::cout << "sensor config:\n";
97 std::cout << "{\n";
Patrick Venturec54fbd82018-10-30 19:40:05 -070098 for (const auto& pair : sensorConfig)
James Feist7136a5a2018-07-19 09:52:05 -070099 {
100
101 std::cout << "\t{" << pair.first << ",\n\t\t{";
102 std::cout << pair.second.type << ", ";
Patrick Venture69c51062019-02-11 09:46:03 -0800103 std::cout << pair.second.readPath << ", ";
104 std::cout << pair.second.writePath << ", ";
James Feist7136a5a2018-07-19 09:52:05 -0700105 std::cout << pair.second.min << ", ";
106 std::cout << pair.second.max << ", ";
107 std::cout << pair.second.timeout << "},\n\t},\n";
108 }
109 std::cout << "}\n\n";
110 std::cout << "ZoneDetailsConfig\n";
111 std::cout << "{\n";
Patrick Venturec54fbd82018-10-30 19:40:05 -0700112 for (const auto& zone : zoneDetailsConfig)
James Feist7136a5a2018-07-19 09:52:05 -0700113 {
114 std::cout << "\t{" << zone.first << ",\n";
James Feist3484bed2019-02-25 13:28:18 -0800115 std::cout << "\t\t{" << zone.second.minThermalOutput << ", ";
Patrick Venture8e2fdb32019-02-11 09:39:59 -0800116 std::cout << zone.second.failsafePercent << "}\n\t},\n";
James Feist7136a5a2018-07-19 09:52:05 -0700117 }
118 std::cout << "}\n\n";
119 std::cout << "ZoneConfig\n";
120 std::cout << "{\n";
Patrick Venturec54fbd82018-10-30 19:40:05 -0700121 for (const auto& zone : zoneConfig)
James Feist7136a5a2018-07-19 09:52:05 -0700122 {
123 std::cout << "\t{" << zone.first << "\n";
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700124 for (const auto& pidconf : zone.second)
James Feist7136a5a2018-07-19 09:52:05 -0700125 {
126 std::cout << "\t\t{" << pidconf.first << ",\n";
127 std::cout << "\t\t\t{" << pidconf.second.type << ",\n";
128 std::cout << "\t\t\t{";
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700129 for (const auto& input : pidconf.second.inputs)
James Feist7136a5a2018-07-19 09:52:05 -0700130 {
131 std::cout << "\n\t\t\t" << input << ",\n";
132 }
133 std::cout << "\t\t\t}\n";
134 std::cout << "\t\t\t" << pidconf.second.setpoint << ",\n";
James Feist22c257a2018-08-31 14:07:12 -0700135 std::cout << "\t\t\t{" << pidconf.second.pidInfo.ts << ",\n";
Patrick Venture7442c372019-02-11 10:21:05 -0800136 std::cout << "\t\t\t" << pidconf.second.pidInfo.proportionalCoeff
137 << ",\n";
138 std::cout << "\t\t\t" << pidconf.second.pidInfo.integralCoeff
139 << ",\n";
140 std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdOffset
141 << ",\n";
142 std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdGain
143 << ",\n";
144 std::cout << "\t\t\t{" << pidconf.second.pidInfo.integralLimit.min
145 << "," << pidconf.second.pidInfo.integralLimit.max
146 << "},\n";
147 std::cout << "\t\t\t{" << pidconf.second.pidInfo.outLim.min << ","
148 << pidconf.second.pidInfo.outLim.max << "},\n";
149 std::cout << "\t\t\t" << pidconf.second.pidInfo.slewNeg << ",\n";
150 std::cout << "\t\t\t" << pidconf.second.pidInfo.slewPos << ",\n";
James Feist7136a5a2018-07-19 09:52:05 -0700151 std::cout << "\t\t\t}\n\t\t}\n";
152 }
153 std::cout << "\t},\n";
154 }
155 std::cout << "}\n\n";
156}
157
James Feistffd418b2018-11-15 14:46:36 -0800158size_t getZoneIndex(const std::string& name, std::vector<std::string>& zones)
159{
160 auto it = std::find(zones.begin(), zones.end(), name);
161 if (it == zones.end())
162 {
163 zones.emplace_back(name);
164 it = zones.end() - 1;
165 }
166
167 return it - zones.begin();
168}
169
James Feistf0096a02019-02-21 11:25:22 -0800170std::vector<std::string> getSelectedProfiles(sdbusplus::bus::bus& bus)
171{
172 std::vector<std::string> ret;
173 auto mapper =
174 bus.new_method_call("xyz.openbmc_project.ObjectMapper",
175 "/xyz/openbmc_project/object_mapper",
176 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
177 mapper.append("/", 0, std::array<const char*, 1>{thermalControlIface});
178 std::unordered_map<
179 std::string, std::unordered_map<std::string, std::vector<std::string>>>
180 respData;
181
182 try
183 {
184 auto resp = bus.call(mapper);
185 resp.read(respData);
186 }
187 catch (sdbusplus::exception_t&)
188 {
189 // can't do anything without mapper call data
190 throw std::runtime_error("ObjectMapper Call Failure");
191 }
192 if (respData.empty())
193 {
194 // if the user has profiles but doesn't expose the interface to select
195 // one, just go ahead without using profiles
196 return ret;
197 }
198
199 // assumption is that we should only have a small handful of selected
200 // profiles at a time (probably only 1), so calling each individually should
201 // not incur a large cost
202 for (const auto& objectPair : respData)
203 {
204 const std::string& path = objectPair.first;
205 for (const auto& ownerPair : objectPair.second)
206 {
207 const std::string& busName = ownerPair.first;
208 auto getProfile =
209 bus.new_method_call(busName.c_str(), path.c_str(),
210 "org.freedesktop.DBus.Properties", "Get");
211 getProfile.append(thermalControlIface, "Current");
212 std::variant<std::string> variantResp;
213 try
214 {
215 auto resp = bus.call(getProfile);
216 resp.read(variantResp);
217 }
218 catch (sdbusplus::exception_t&)
219 {
220 throw std::runtime_error("Failure getting profile");
221 }
222 std::string mode = std::get<std::string>(variantResp);
223 ret.emplace_back(std::move(mode));
224 }
225 }
226 if constexpr (DEBUG)
227 {
228 std::cout << "Profiles selected: ";
229 for (const auto& profile : ret)
230 {
231 std::cout << profile << " ";
232 }
233 std::cout << "\n";
234 }
235 return ret;
236}
237
James Feist991ebd82020-07-21 11:14:52 -0700238int eventHandler(sd_bus_message* m, void* context, sd_bus_error*)
James Feist7136a5a2018-07-19 09:52:05 -0700239{
James Feist1fe08952019-05-07 09:17:16 -0700240
James Feist991ebd82020-07-21 11:14:52 -0700241 if (context == nullptr || m == nullptr)
James Feist1fe08952019-05-07 09:17:16 -0700242 {
243 throw std::runtime_error("Invalid match");
244 }
James Feist991ebd82020-07-21 11:14:52 -0700245
246 // we skip associations because the mapper populates these, not the sensors
247 const std::array<const char*, 1> skipList = {
248 "xyz.openbmc_project.Association"};
249
250 sdbusplus::message::message message(m);
251 if (std::string(message.get_member()) == "InterfacesAdded")
252 {
253 sdbusplus::message::object_path path;
254 std::unordered_map<
255 std::string,
256 std::unordered_map<std::string, std::variant<Associations, bool>>>
257 data;
258
259 message.read(path, data);
260
261 for (const char* skip : skipList)
262 {
263 auto find = data.find(skip);
264 if (find != data.end())
265 {
266 data.erase(find);
267 if (data.empty())
268 {
269 return 1;
270 }
271 }
272 }
273 }
274
James Feist1fe08952019-05-07 09:17:16 -0700275 boost::asio::steady_timer* timer =
276 static_cast<boost::asio::steady_timer*>(context);
277
278 // do a brief sleep as we tend to get a bunch of these events at
279 // once
280 timer->expires_after(std::chrono::seconds(2));
281 timer->async_wait([](const boost::system::error_code ec) {
282 if (ec == boost::asio::error::operation_aborted)
283 {
284 /* another timer started*/
285 return;
286 }
287
288 std::cout << "New configuration detected, reloading\n.";
Yong Li298a95c2020-04-07 15:11:02 +0800289 tryRestartControlLoops();
James Feist1fe08952019-05-07 09:17:16 -0700290 });
291
292 return 1;
293}
294
295void createMatches(sdbusplus::bus::bus& bus, boost::asio::steady_timer& timer)
296{
297 // this is a list because the matches can't be moved
298 static std::list<sdbusplus::bus::match::match> matches;
299
James Feist3987c8b2019-05-13 10:43:17 -0700300 const std::array<std::string, 4> interfaces = {
301 thermalControlIface, pidConfigurationInterface,
302 pidZoneConfigurationInterface, stepwiseConfigurationInterface};
James Feist1fe08952019-05-07 09:17:16 -0700303
304 // this list only needs to be created once
305 if (!matches.empty())
306 {
307 return;
308 }
309
310 // we restart when the configuration changes or there are new sensors
311 for (const auto& interface : interfaces)
312 {
313 matches.emplace_back(
314 bus,
315 "type='signal',member='PropertiesChanged',arg0namespace='" +
316 interface + "'",
317 eventHandler, &timer);
318 }
319 matches.emplace_back(
320 bus,
321 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
322 "sensors/'",
323 eventHandler, &timer);
324}
325
Jason Ling6fc301f2020-07-23 12:39:57 -0700326/**
327 * retrieve an attribute from the pid configuration map
328 * @param[in] base - the PID configuration map, keys are the attributes and
329 * value is the variant associated with that attribute.
330 * @param attributeName - the name of the attribute
331 * @return a variant holding the value associated with a key
332 * @throw runtime_error : attributeName is not in base
333 */
334inline DbusVariantType getPIDAttribute(
335 const std::unordered_map<std::string, DbusVariantType>& base,
336 const std::string& attributeName)
337{
338 auto search = base.find(attributeName);
339 if (search == base.end())
340 {
341 throw std::runtime_error("missing attribute " + attributeName);
342 }
343 return search->second;
344}
345
James Feist5ec20272019-07-10 11:59:57 -0700346void populatePidInfo(
347 sdbusplus::bus::bus& bus,
348 const std::unordered_map<std::string, DbusVariantType>& base,
349 struct conf::ControllerInfo& info, const std::string* thresholdProperty)
350{
Jason Ling6fc301f2020-07-23 12:39:57 -0700351 info.type = std::get<std::string>(getPIDAttribute(base, "Class"));
James Feist5ec20272019-07-10 11:59:57 -0700352 if (info.type == "fan")
353 {
354 info.setpoint = 0;
355 }
356 else
357 {
Jason Ling6fc301f2020-07-23 12:39:57 -0700358 info.setpoint = std::visit(VariantToDoubleVisitor(),
359 getPIDAttribute(base, "SetPoint"));
James Feist5ec20272019-07-10 11:59:57 -0700360 }
361
362 if (thresholdProperty != nullptr)
363 {
364 std::string interface;
365 if (*thresholdProperty == "WarningHigh" ||
366 *thresholdProperty == "WarningLow")
367 {
368 interface = thresholds::warningInterface;
369 }
370 else
371 {
372 interface = thresholds::criticalInterface;
373 }
374 const std::string& path = sensorConfig[info.inputs.front()].readPath;
375
376 DbusHelper helper;
377 std::string service = helper.getService(bus, interface, path);
378 double reading = 0;
379 try
380 {
381 helper.getProperty(bus, service, path, interface,
382 *thresholdProperty, reading);
383 }
384 catch (const sdbusplus::exception::SdBusError& ex)
385 {
386 // unsupported threshold, leaving reading at 0
387 }
388
389 info.setpoint += reading;
390 }
391
392 info.pidInfo.ts = 1.0; // currently unused
Jason Ling6fc301f2020-07-23 12:39:57 -0700393 info.pidInfo.proportionalCoeff = std::visit(
394 VariantToDoubleVisitor(), getPIDAttribute(base, "PCoefficient"));
395 info.pidInfo.integralCoeff = std::visit(
396 VariantToDoubleVisitor(), getPIDAttribute(base, "ICoefficient"));
397 info.pidInfo.feedFwdOffset = std::visit(
398 VariantToDoubleVisitor(), getPIDAttribute(base, "FFOffCoefficient"));
399 info.pidInfo.feedFwdGain = std::visit(
400 VariantToDoubleVisitor(), getPIDAttribute(base, "FFGainCoefficient"));
401 info.pidInfo.integralLimit.max = std::visit(
402 VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMax"));
403 info.pidInfo.integralLimit.min = std::visit(
404 VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMin"));
405 info.pidInfo.outLim.max = std::visit(VariantToDoubleVisitor(),
406 getPIDAttribute(base, "OutLimitMax"));
407 info.pidInfo.outLim.min = std::visit(VariantToDoubleVisitor(),
408 getPIDAttribute(base, "OutLimitMin"));
James Feist5ec20272019-07-10 11:59:57 -0700409 info.pidInfo.slewNeg =
Jason Ling6fc301f2020-07-23 12:39:57 -0700410 std::visit(VariantToDoubleVisitor(), getPIDAttribute(base, "SlewNeg"));
James Feist5ec20272019-07-10 11:59:57 -0700411 info.pidInfo.slewPos =
Jason Ling6fc301f2020-07-23 12:39:57 -0700412 std::visit(VariantToDoubleVisitor(), getPIDAttribute(base, "SlewPos"));
James Feist5ec20272019-07-10 11:59:57 -0700413 double negativeHysteresis = 0;
414 double positiveHysteresis = 0;
415
416 auto findNeg = base.find("NegativeHysteresis");
417 auto findPos = base.find("PositiveHysteresis");
418
419 if (findNeg != base.end())
420 {
421 negativeHysteresis =
422 std::visit(VariantToDoubleVisitor(), findNeg->second);
423 }
424
425 if (findPos != base.end())
426 {
427 positiveHysteresis =
428 std::visit(VariantToDoubleVisitor(), findPos->second);
429 }
James Feist5ec20272019-07-10 11:59:57 -0700430 info.pidInfo.negativeHysteresis = negativeHysteresis;
431 info.pidInfo.positiveHysteresis = positiveHysteresis;
432}
433
James Feist1fe08952019-05-07 09:17:16 -0700434bool init(sdbusplus::bus::bus& bus, boost::asio::steady_timer& timer)
435{
436
437 sensorConfig.clear();
438 zoneConfig.clear();
439 zoneDetailsConfig.clear();
440
441 createMatches(bus, timer);
442
James Feist22c257a2018-08-31 14:07:12 -0700443 using DbusVariantType =
James Feist1f802f52019-02-08 13:51:43 -0800444 std::variant<uint64_t, int64_t, double, std::string,
445 std::vector<std::string>, std::vector<double>>;
James Feist22c257a2018-08-31 14:07:12 -0700446
James Feist7136a5a2018-07-19 09:52:05 -0700447 using ManagedObjectType = std::unordered_map<
448 sdbusplus::message::object_path,
James Feist22c257a2018-08-31 14:07:12 -0700449 std::unordered_map<std::string,
450 std::unordered_map<std::string, DbusVariantType>>>;
James Feist64f072a2018-08-10 16:39:24 -0700451
James Feist7136a5a2018-07-19 09:52:05 -0700452 auto mapper =
453 bus.new_method_call("xyz.openbmc_project.ObjectMapper",
454 "/xyz/openbmc_project/object_mapper",
455 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feist26e8c6a2018-10-25 10:38:26 -0700456 mapper.append("/", 0,
James Feist3987c8b2019-05-13 10:43:17 -0700457 std::array<const char*, 6>{objectManagerInterface,
458 pidConfigurationInterface,
459 pidZoneConfigurationInterface,
460 stepwiseConfigurationInterface,
461 sensorInterface, pwmInterface});
James Feist7136a5a2018-07-19 09:52:05 -0700462 std::unordered_map<
463 std::string, std::unordered_map<std::string, std::vector<std::string>>>
464 respData;
James Feist22c257a2018-08-31 14:07:12 -0700465 try
466 {
467 auto resp = bus.call(mapper);
James Feist22c257a2018-08-31 14:07:12 -0700468 resp.read(respData);
469 }
470 catch (sdbusplus::exception_t&)
471 {
472 // can't do anything without mapper call data
473 throw std::runtime_error("ObjectMapper Call Failure");
474 }
James Feist7136a5a2018-07-19 09:52:05 -0700475
James Feist7136a5a2018-07-19 09:52:05 -0700476 if (respData.empty())
477 {
James Feist22c257a2018-08-31 14:07:12 -0700478 // can't do anything without mapper call data
James Feist7136a5a2018-07-19 09:52:05 -0700479 throw std::runtime_error("No configuration data available from Mapper");
480 }
481 // create a map of pair of <has pid configuration, ObjectManager path>
482 std::unordered_map<std::string, std::pair<bool, std::string>> owners;
483 // and a map of <path, interface> for sensors
484 std::unordered_map<std::string, std::string> sensors;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700485 for (const auto& objectPair : respData)
James Feist7136a5a2018-07-19 09:52:05 -0700486 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700487 for (const auto& ownerPair : objectPair.second)
James Feist7136a5a2018-07-19 09:52:05 -0700488 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700489 auto& owner = owners[ownerPair.first];
490 for (const std::string& interface : ownerPair.second)
James Feist7136a5a2018-07-19 09:52:05 -0700491 {
492
493 if (interface == objectManagerInterface)
494 {
495 owner.second = objectPair.first;
496 }
497 if (interface == pidConfigurationInterface ||
James Feist22c257a2018-08-31 14:07:12 -0700498 interface == pidZoneConfigurationInterface ||
499 interface == stepwiseConfigurationInterface)
James Feist7136a5a2018-07-19 09:52:05 -0700500 {
501 owner.first = true;
502 }
503 if (interface == sensorInterface || interface == pwmInterface)
504 {
505 // we're not interested in pwm sensors, just pwm control
506 if (interface == sensorInterface &&
507 objectPair.first.find("pwm") != std::string::npos)
508 {
509 continue;
510 }
511 sensors[objectPair.first] = interface;
512 }
513 }
514 }
515 }
516 ManagedObjectType configurations;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700517 for (const auto& owner : owners)
James Feist7136a5a2018-07-19 09:52:05 -0700518 {
519 // skip if no pid configuration (means probably a sensor)
520 if (!owner.second.first)
521 {
522 continue;
523 }
524 auto endpoint = bus.new_method_call(
525 owner.first.c_str(), owner.second.second.c_str(),
526 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist22c257a2018-08-31 14:07:12 -0700527 ManagedObjectType configuration;
528 try
James Feist7136a5a2018-07-19 09:52:05 -0700529 {
James Feist22c257a2018-08-31 14:07:12 -0700530 auto responce = bus.call(endpoint);
James Feist22c257a2018-08-31 14:07:12 -0700531 responce.read(configuration);
532 }
533 catch (sdbusplus::exception_t&)
534 {
535 // this shouldn't happen, probably means daemon crashed
James Feist7136a5a2018-07-19 09:52:05 -0700536 throw std::runtime_error("Error getting managed objects from " +
537 owner.first);
538 }
James Feist22c257a2018-08-31 14:07:12 -0700539
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700540 for (auto& pathPair : configuration)
James Feist7136a5a2018-07-19 09:52:05 -0700541 {
542 if (pathPair.second.find(pidConfigurationInterface) !=
543 pathPair.second.end() ||
544 pathPair.second.find(pidZoneConfigurationInterface) !=
James Feist22c257a2018-08-31 14:07:12 -0700545 pathPair.second.end() ||
546 pathPair.second.find(stepwiseConfigurationInterface) !=
James Feist7136a5a2018-07-19 09:52:05 -0700547 pathPair.second.end())
548 {
549 configurations.emplace(pathPair);
550 }
James Feistf0096a02019-02-21 11:25:22 -0800551 }
552 }
553
554 // remove controllers from config that aren't in the current profile(s)
James Feist3987c8b2019-05-13 10:43:17 -0700555 std::vector<std::string> selectedProfiles = getSelectedProfiles(bus);
556 if (selectedProfiles.size())
James Feistf0096a02019-02-21 11:25:22 -0800557 {
James Feist3987c8b2019-05-13 10:43:17 -0700558 for (auto pathIt = configurations.begin();
559 pathIt != configurations.end();)
James Feistf0096a02019-02-21 11:25:22 -0800560 {
James Feist3987c8b2019-05-13 10:43:17 -0700561 for (auto confIt = pathIt->second.begin();
562 confIt != pathIt->second.end();)
James Feistf0096a02019-02-21 11:25:22 -0800563 {
James Feist3987c8b2019-05-13 10:43:17 -0700564 auto profilesFind = confIt->second.find("Profiles");
565 if (profilesFind == confIt->second.end())
James Feistf0096a02019-02-21 11:25:22 -0800566 {
James Feist3987c8b2019-05-13 10:43:17 -0700567 confIt++;
568 continue; // if no profiles selected, apply always
569 }
570 auto profiles =
571 std::get<std::vector<std::string>>(profilesFind->second);
572 if (profiles.empty())
573 {
574 confIt++;
575 continue;
576 }
577
578 bool found = false;
579 for (const std::string& profile : profiles)
580 {
581 if (std::find(selectedProfiles.begin(),
582 selectedProfiles.end(),
583 profile) != selectedProfiles.end())
584 {
585 found = true;
586 break;
587 }
588 }
589 if (found)
590 {
591 confIt++;
James Feistf0096a02019-02-21 11:25:22 -0800592 }
593 else
594 {
James Feist3987c8b2019-05-13 10:43:17 -0700595 confIt = pathIt->second.erase(confIt);
James Feistf0096a02019-02-21 11:25:22 -0800596 }
597 }
James Feist3987c8b2019-05-13 10:43:17 -0700598 if (pathIt->second.empty())
James Feistf0096a02019-02-21 11:25:22 -0800599 {
James Feist3987c8b2019-05-13 10:43:17 -0700600 pathIt = configurations.erase(pathIt);
James Feistf0096a02019-02-21 11:25:22 -0800601 }
James Feist3987c8b2019-05-13 10:43:17 -0700602 else
James Feistf0096a02019-02-21 11:25:22 -0800603 {
James Feist3987c8b2019-05-13 10:43:17 -0700604 pathIt++;
James Feistf0096a02019-02-21 11:25:22 -0800605 }
James Feist7136a5a2018-07-19 09:52:05 -0700606 }
607 }
James Feist8c3c51e2018-08-08 16:31:43 -0700608
609 // on dbus having an index field is a bit strange, so randomly
610 // assign index based on name property
James Feistffd418b2018-11-15 14:46:36 -0800611 std::vector<std::string> foundZones;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700612 for (const auto& configuration : configurations)
James Feist7136a5a2018-07-19 09:52:05 -0700613 {
614 auto findZone =
615 configuration.second.find(pidZoneConfigurationInterface);
616 if (findZone != configuration.second.end())
617 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700618 const auto& zone = findZone->second;
James Feistffd418b2018-11-15 14:46:36 -0800619
James Feist1f802f52019-02-08 13:51:43 -0800620 const std::string& name = std::get<std::string>(zone.at("Name"));
James Feistffd418b2018-11-15 14:46:36 -0800621 size_t index = getZoneIndex(name, foundZones);
James Feist8c3c51e2018-08-08 16:31:43 -0700622
Patrick Venturec54fbd82018-10-30 19:40:05 -0700623 auto& details = zoneDetailsConfig[index];
James Feist3484bed2019-02-25 13:28:18 -0800624 details.minThermalOutput = std::visit(VariantToDoubleVisitor(),
625 zone.at("MinThermalOutput"));
Patrick Venture8e2fdb32019-02-11 09:39:59 -0800626 details.failsafePercent = std::visit(VariantToDoubleVisitor(),
James Feist1f802f52019-02-08 13:51:43 -0800627 zone.at("FailSafePercent"));
James Feist7136a5a2018-07-19 09:52:05 -0700628 }
629 auto findBase = configuration.second.find(pidConfigurationInterface);
James Feist22c257a2018-08-31 14:07:12 -0700630 if (findBase != configuration.second.end())
James Feist7136a5a2018-07-19 09:52:05 -0700631 {
James Feist8c3c51e2018-08-08 16:31:43 -0700632
James Feist22c257a2018-08-31 14:07:12 -0700633 const auto& base =
634 configuration.second.at(pidConfigurationInterface);
635 const std::vector<std::string>& zones =
James Feist1f802f52019-02-08 13:51:43 -0800636 std::get<std::vector<std::string>>(base.at("Zones"));
James Feist22c257a2018-08-31 14:07:12 -0700637 for (const std::string& zone : zones)
James Feist7136a5a2018-07-19 09:52:05 -0700638 {
James Feistffd418b2018-11-15 14:46:36 -0800639 size_t index = getZoneIndex(zone, foundZones);
James Feistf81f2882019-02-26 11:26:36 -0800640 conf::PIDConf& conf = zoneConfig[index];
James Feist50fdfe32018-09-24 15:51:09 -0700641
642 std::vector<std::string> sensorNames =
James Feist1f802f52019-02-08 13:51:43 -0800643 std::get<std::vector<std::string>>(base.at("Inputs"));
James Feist50fdfe32018-09-24 15:51:09 -0700644 auto findOutputs =
645 base.find("Outputs"); // currently only fans have outputs
646 if (findOutputs != base.end())
647 {
648 std::vector<std::string> outputs =
James Feist1f802f52019-02-08 13:51:43 -0800649 std::get<std::vector<std::string>>(findOutputs->second);
James Feist50fdfe32018-09-24 15:51:09 -0700650 sensorNames.insert(sensorNames.end(), outputs.begin(),
651 outputs.end());
652 }
James Feist1738e2a2019-02-04 15:57:03 -0800653
James Feist50fdfe32018-09-24 15:51:09 -0700654 std::vector<std::string> inputs;
James Feist1738e2a2019-02-04 15:57:03 -0800655 std::vector<std::pair<std::string, std::string>>
656 sensorInterfaces;
James Feist50fdfe32018-09-24 15:51:09 -0700657 for (const std::string& sensorName : sensorNames)
658 {
659 std::string name = sensorName;
660 // replace spaces with underscores to be legal on dbus
661 std::replace(name.begin(), name.end(), ' ', '_');
James Feist1738e2a2019-02-04 15:57:03 -0800662 findSensors(sensors, name, sensorInterfaces);
663 }
James Feist50fdfe32018-09-24 15:51:09 -0700664
James Feist1738e2a2019-02-04 15:57:03 -0800665 for (const auto& sensorPathIfacePair : sensorInterfaces)
666 {
667
James Feist50fdfe32018-09-24 15:51:09 -0700668 if (sensorPathIfacePair.second == sensorInterface)
669 {
James Feist1738e2a2019-02-04 15:57:03 -0800670 size_t idx =
671 sensorPathIfacePair.first.find_last_of("/") + 1;
672 std::string shortName =
673 sensorPathIfacePair.first.substr(idx);
674
675 inputs.push_back(shortName);
676 auto& config = sensorConfig[shortName];
James Feist1f802f52019-02-08 13:51:43 -0800677 config.type = std::get<std::string>(base.at("Class"));
Patrick Venture69c51062019-02-11 09:46:03 -0800678 config.readPath = sensorPathIfacePair.first;
James Feist50fdfe32018-09-24 15:51:09 -0700679 // todo: maybe un-hardcode this if we run into slower
680 // timeouts with sensors
681 if (config.type == "temp")
682 {
James Feist2642cb52019-02-25 13:00:16 -0800683 config.timeout = 0;
James Feist3433cb62019-11-11 16:12:45 -0800684 config.ignoreDbusMinMax = true;
James Feist50fdfe32018-09-24 15:51:09 -0700685 }
686 }
687 else if (sensorPathIfacePair.second == pwmInterface)
688 {
689 // copy so we can modify it
690 for (std::string otherSensor : sensorNames)
691 {
James Feist1738e2a2019-02-04 15:57:03 -0800692 std::replace(otherSensor.begin(), otherSensor.end(),
693 ' ', '_');
694 if (sensorPathIfacePair.first.find(otherSensor) !=
695 std::string::npos)
James Feist50fdfe32018-09-24 15:51:09 -0700696 {
697 continue;
698 }
James Feist1738e2a2019-02-04 15:57:03 -0800699
Patrick Venturec54fbd82018-10-30 19:40:05 -0700700 auto& config = sensorConfig[otherSensor];
Patrick Venture69c51062019-02-11 09:46:03 -0800701 config.writePath = sensorPathIfacePair.first;
James Feist50fdfe32018-09-24 15:51:09 -0700702 // todo: un-hardcode this if there are fans with
703 // different ranges
704 config.max = 255;
705 config.min = 0;
706 }
707 }
708 }
James Feist1738e2a2019-02-04 15:57:03 -0800709
James Feist11d243d2019-06-24 16:18:40 -0700710 // if the sensors aren't available in the current state, don't
711 // add them to the configuration.
712 if (inputs.empty())
713 {
714 continue;
715 }
716
James Feist5ec20272019-07-10 11:59:57 -0700717 std::string offsetType;
James Feist50fdfe32018-09-24 15:51:09 -0700718
James Feist5ec20272019-07-10 11:59:57 -0700719 // SetPointOffset is a threshold value to pull from the sensor
720 // to apply an offset. For upper thresholds this means the
721 // setpoint is usually negative.
722 auto findSetpointOffset = base.find("SetPointOffset");
723 if (findSetpointOffset != base.end())
James Feist22c257a2018-08-31 14:07:12 -0700724 {
James Feist5ec20272019-07-10 11:59:57 -0700725 offsetType =
726 std::get<std::string>(findSetpointOffset->second);
727 if (std::find(thresholds::types.begin(),
728 thresholds::types.end(),
729 offsetType) == thresholds::types.end())
730 {
731 throw std::runtime_error("Unsupported type: " +
732 offsetType);
733 }
734 }
735
736 if (offsetType.empty())
737 {
738 struct conf::ControllerInfo& info =
739 conf[std::get<std::string>(base.at("Name"))];
740 info.inputs = std::move(inputs);
741 populatePidInfo(bus, base, info, nullptr);
James Feist22c257a2018-08-31 14:07:12 -0700742 }
743 else
744 {
James Feist5ec20272019-07-10 11:59:57 -0700745 // we have to split up the inputs, as in practice t-control
746 // values will differ, making setpoints differ
747 for (const std::string& input : inputs)
748 {
749 struct conf::ControllerInfo& info = conf[input];
750 info.inputs.emplace_back(input);
751 populatePidInfo(bus, base, info, &offsetType);
752 }
James Feist22c257a2018-08-31 14:07:12 -0700753 }
James Feist22c257a2018-08-31 14:07:12 -0700754 }
755 }
756 auto findStepwise =
757 configuration.second.find(stepwiseConfigurationInterface);
758 if (findStepwise != configuration.second.end())
759 {
760 const auto& base = findStepwise->second;
761 const std::vector<std::string>& zones =
James Feist1f802f52019-02-08 13:51:43 -0800762 std::get<std::vector<std::string>>(base.at("Zones"));
James Feist22c257a2018-08-31 14:07:12 -0700763 for (const std::string& zone : zones)
764 {
James Feistffd418b2018-11-15 14:46:36 -0800765 size_t index = getZoneIndex(zone, foundZones);
James Feistf81f2882019-02-26 11:26:36 -0800766 conf::PIDConf& conf = zoneConfig[index];
James Feist50fdfe32018-09-24 15:51:09 -0700767
768 std::vector<std::string> inputs;
769 std::vector<std::string> sensorNames =
James Feist1f802f52019-02-08 13:51:43 -0800770 std::get<std::vector<std::string>>(base.at("Inputs"));
James Feist50fdfe32018-09-24 15:51:09 -0700771
James Feist1738e2a2019-02-04 15:57:03 -0800772 bool sensorFound = false;
James Feist50fdfe32018-09-24 15:51:09 -0700773 for (const std::string& sensorName : sensorNames)
774 {
775 std::string name = sensorName;
776 // replace spaces with underscores to be legal on dbus
777 std::replace(name.begin(), name.end(), ' ', '_');
James Feist1738e2a2019-02-04 15:57:03 -0800778 std::vector<std::pair<std::string, std::string>>
779 sensorPathIfacePairs;
James Feist50fdfe32018-09-24 15:51:09 -0700780
James Feist1738e2a2019-02-04 15:57:03 -0800781 if (!findSensors(sensors, name, sensorPathIfacePairs))
James Feist50fdfe32018-09-24 15:51:09 -0700782 {
James Feist50fdfe32018-09-24 15:51:09 -0700783 break;
784 }
785
James Feist1738e2a2019-02-04 15:57:03 -0800786 for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
787 {
788 size_t idx =
789 sensorPathIfacePair.first.find_last_of("/") + 1;
790 std::string shortName =
791 sensorPathIfacePair.first.substr(idx);
James Feist50fdfe32018-09-24 15:51:09 -0700792
James Feist1738e2a2019-02-04 15:57:03 -0800793 inputs.push_back(shortName);
794 auto& config = sensorConfig[shortName];
Patrick Venture69c51062019-02-11 09:46:03 -0800795 config.readPath = sensorPathIfacePair.first;
James Feist1738e2a2019-02-04 15:57:03 -0800796 config.type = "temp";
James Feist3660b382019-11-11 16:29:19 -0800797 config.ignoreDbusMinMax = true;
James Feist1738e2a2019-02-04 15:57:03 -0800798 // todo: maybe un-hardcode this if we run into slower
799 // timeouts with sensors
800
James Feist2642cb52019-02-25 13:00:16 -0800801 config.timeout = 0;
James Feist1738e2a2019-02-04 15:57:03 -0800802 sensorFound = true;
803 }
James Feist50fdfe32018-09-24 15:51:09 -0700804 }
805 if (!sensorFound)
806 {
807 continue;
808 }
James Feistf81f2882019-02-26 11:26:36 -0800809 struct conf::ControllerInfo& info =
James Feist1f802f52019-02-08 13:51:43 -0800810 conf[std::get<std::string>(base.at("Name"))];
James Feist50fdfe32018-09-24 15:51:09 -0700811 info.inputs = std::move(inputs);
812
James Feist22c257a2018-08-31 14:07:12 -0700813 info.type = "stepwise";
814 info.stepwiseInfo.ts = 1.0; // currently unused
James Feist3dfaafd2018-09-20 15:46:58 -0700815 info.stepwiseInfo.positiveHysteresis = 0.0;
816 info.stepwiseInfo.negativeHysteresis = 0.0;
James Feist608304d2019-02-25 10:01:42 -0800817 std::string subtype = std::get<std::string>(base.at("Class"));
818
819 info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
James Feist3dfaafd2018-09-20 15:46:58 -0700820 auto findPosHyst = base.find("PositiveHysteresis");
821 auto findNegHyst = base.find("NegativeHysteresis");
822 if (findPosHyst != base.end())
823 {
James Feist1f802f52019-02-08 13:51:43 -0800824 info.stepwiseInfo.positiveHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -0800825 VariantToDoubleVisitor(), findPosHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -0700826 }
827 if (findNegHyst != base.end())
828 {
James Feist5782ab82019-04-02 08:38:48 -0700829 info.stepwiseInfo.negativeHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -0800830 VariantToDoubleVisitor(), findNegHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -0700831 }
James Feist22c257a2018-08-31 14:07:12 -0700832 std::vector<double> readings =
James Feist1f802f52019-02-08 13:51:43 -0800833 std::get<std::vector<double>>(base.at("Reading"));
James Feist22c257a2018-08-31 14:07:12 -0700834 if (readings.size() > ec::maxStepwisePoints)
835 {
836 throw std::invalid_argument("Too many stepwise points.");
837 }
838 if (readings.empty())
839 {
840 throw std::invalid_argument(
841 "Must have one stepwise point.");
842 }
843 std::copy(readings.begin(), readings.end(),
844 info.stepwiseInfo.reading);
845 if (readings.size() < ec::maxStepwisePoints)
846 {
847 info.stepwiseInfo.reading[readings.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800848 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -0700849 }
850 std::vector<double> outputs =
James Feist1f802f52019-02-08 13:51:43 -0800851 std::get<std::vector<double>>(base.at("Output"));
James Feist22c257a2018-08-31 14:07:12 -0700852 if (readings.size() != outputs.size())
853 {
854 throw std::invalid_argument(
855 "Outputs size must match readings");
856 }
857 std::copy(outputs.begin(), outputs.end(),
858 info.stepwiseInfo.output);
859 if (outputs.size() < ec::maxStepwisePoints)
860 {
861 info.stepwiseInfo.output[outputs.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800862 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -0700863 }
James Feist7136a5a2018-07-19 09:52:05 -0700864 }
865 }
866 }
James Feistf0096a02019-02-21 11:25:22 -0800867 if constexpr (DEBUG)
James Feist7136a5a2018-07-19 09:52:05 -0700868 {
869 debugPrint();
870 }
James Feistc959c422018-11-01 12:33:40 -0700871 if (zoneConfig.empty() || zoneDetailsConfig.empty())
James Feist50fdfe32018-09-24 15:51:09 -0700872 {
James Feist1fe08952019-05-07 09:17:16 -0700873 std::cerr
874 << "No fan zones, application pausing until new configuration\n";
875 return false;
James Feist50fdfe32018-09-24 15:51:09 -0700876 }
James Feist1fe08952019-05-07 09:17:16 -0700877 return true;
James Feist7136a5a2018-07-19 09:52:05 -0700878}
879} // namespace dbus_configuration