blob: 228248fccdc2bd814d55785fba04c091e79f6692 [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 Feist5ec20272019-07-10 11:59:57 -070053namespace thresholds
54{
55constexpr const char* warningInterface =
56 "xyz.openbmc_project.Sensor.Threshold.Warning";
57constexpr const char* criticalInterface =
58 "xyz.openbmc_project.Sensor.Threshold.Critical";
59const std::array<const char*, 4> types = {"CriticalLow", "CriticalHigh",
60 "WarningLow", "WarningHigh"};
61
62} // namespace thresholds
63
James Feist7136a5a2018-07-19 09:52:05 -070064namespace dbus_configuration
65{
66
James Feist5ec20272019-07-10 11:59:57 -070067using DbusVariantType =
68 std::variant<uint64_t, int64_t, double, std::string,
69 std::vector<std::string>, std::vector<double>>;
70
James Feist1738e2a2019-02-04 15:57:03 -080071bool findSensors(const std::unordered_map<std::string, std::string>& sensors,
72 const std::string& search,
73 std::vector<std::pair<std::string, std::string>>& matches)
James Feist7136a5a2018-07-19 09:52:05 -070074{
James Feist1738e2a2019-02-04 15:57:03 -080075 std::smatch match;
Jae Hyun Yoo1a704dc2020-06-04 15:00:10 -070076 std::regex reg(search + '$');
James Feist1738e2a2019-02-04 15:57:03 -080077 for (const auto& sensor : sensors)
James Feist7136a5a2018-07-19 09:52:05 -070078 {
James Feist1738e2a2019-02-04 15:57:03 -080079 if (std::regex_search(sensor.first, match, reg))
80 {
81 matches.push_back(sensor);
82 }
James Feist7136a5a2018-07-19 09:52:05 -070083 }
Patrick Venture107a25d2018-10-13 14:08:09 -070084
James Feist1738e2a2019-02-04 15:57:03 -080085 return matches.size() > 0;
James Feist7136a5a2018-07-19 09:52:05 -070086}
87
88// this function prints the configuration into a form similar to the cpp
89// generated code to help in verification, should be turned off during normal
90// use
91void debugPrint(void)
92{
93 // print sensor config
94 std::cout << "sensor config:\n";
95 std::cout << "{\n";
Patrick Venturec54fbd82018-10-30 19:40:05 -070096 for (const auto& pair : sensorConfig)
James Feist7136a5a2018-07-19 09:52:05 -070097 {
98
99 std::cout << "\t{" << pair.first << ",\n\t\t{";
100 std::cout << pair.second.type << ", ";
Patrick Venture69c51062019-02-11 09:46:03 -0800101 std::cout << pair.second.readPath << ", ";
102 std::cout << pair.second.writePath << ", ";
James Feist7136a5a2018-07-19 09:52:05 -0700103 std::cout << pair.second.min << ", ";
104 std::cout << pair.second.max << ", ";
105 std::cout << pair.second.timeout << "},\n\t},\n";
106 }
107 std::cout << "}\n\n";
108 std::cout << "ZoneDetailsConfig\n";
109 std::cout << "{\n";
Patrick Venturec54fbd82018-10-30 19:40:05 -0700110 for (const auto& zone : zoneDetailsConfig)
James Feist7136a5a2018-07-19 09:52:05 -0700111 {
112 std::cout << "\t{" << zone.first << ",\n";
James Feist3484bed2019-02-25 13:28:18 -0800113 std::cout << "\t\t{" << zone.second.minThermalOutput << ", ";
Patrick Venture8e2fdb32019-02-11 09:39:59 -0800114 std::cout << zone.second.failsafePercent << "}\n\t},\n";
James Feist7136a5a2018-07-19 09:52:05 -0700115 }
116 std::cout << "}\n\n";
117 std::cout << "ZoneConfig\n";
118 std::cout << "{\n";
Patrick Venturec54fbd82018-10-30 19:40:05 -0700119 for (const auto& zone : zoneConfig)
James Feist7136a5a2018-07-19 09:52:05 -0700120 {
121 std::cout << "\t{" << zone.first << "\n";
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700122 for (const auto& pidconf : zone.second)
James Feist7136a5a2018-07-19 09:52:05 -0700123 {
124 std::cout << "\t\t{" << pidconf.first << ",\n";
125 std::cout << "\t\t\t{" << pidconf.second.type << ",\n";
126 std::cout << "\t\t\t{";
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700127 for (const auto& input : pidconf.second.inputs)
James Feist7136a5a2018-07-19 09:52:05 -0700128 {
129 std::cout << "\n\t\t\t" << input << ",\n";
130 }
131 std::cout << "\t\t\t}\n";
132 std::cout << "\t\t\t" << pidconf.second.setpoint << ",\n";
James Feist22c257a2018-08-31 14:07:12 -0700133 std::cout << "\t\t\t{" << pidconf.second.pidInfo.ts << ",\n";
Patrick Venture7442c372019-02-11 10:21:05 -0800134 std::cout << "\t\t\t" << pidconf.second.pidInfo.proportionalCoeff
135 << ",\n";
136 std::cout << "\t\t\t" << pidconf.second.pidInfo.integralCoeff
137 << ",\n";
138 std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdOffset
139 << ",\n";
140 std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdGain
141 << ",\n";
142 std::cout << "\t\t\t{" << pidconf.second.pidInfo.integralLimit.min
143 << "," << pidconf.second.pidInfo.integralLimit.max
144 << "},\n";
145 std::cout << "\t\t\t{" << pidconf.second.pidInfo.outLim.min << ","
146 << pidconf.second.pidInfo.outLim.max << "},\n";
147 std::cout << "\t\t\t" << pidconf.second.pidInfo.slewNeg << ",\n";
148 std::cout << "\t\t\t" << pidconf.second.pidInfo.slewPos << ",\n";
James Feist7136a5a2018-07-19 09:52:05 -0700149 std::cout << "\t\t\t}\n\t\t}\n";
150 }
151 std::cout << "\t},\n";
152 }
153 std::cout << "}\n\n";
154}
155
James Feistffd418b2018-11-15 14:46:36 -0800156size_t getZoneIndex(const std::string& name, std::vector<std::string>& zones)
157{
158 auto it = std::find(zones.begin(), zones.end(), name);
159 if (it == zones.end())
160 {
161 zones.emplace_back(name);
162 it = zones.end() - 1;
163 }
164
165 return it - zones.begin();
166}
167
James Feistf0096a02019-02-21 11:25:22 -0800168std::vector<std::string> getSelectedProfiles(sdbusplus::bus::bus& bus)
169{
170 std::vector<std::string> ret;
171 auto mapper =
172 bus.new_method_call("xyz.openbmc_project.ObjectMapper",
173 "/xyz/openbmc_project/object_mapper",
174 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
175 mapper.append("/", 0, std::array<const char*, 1>{thermalControlIface});
176 std::unordered_map<
177 std::string, std::unordered_map<std::string, std::vector<std::string>>>
178 respData;
179
180 try
181 {
182 auto resp = bus.call(mapper);
183 resp.read(respData);
184 }
185 catch (sdbusplus::exception_t&)
186 {
187 // can't do anything without mapper call data
188 throw std::runtime_error("ObjectMapper Call Failure");
189 }
190 if (respData.empty())
191 {
192 // if the user has profiles but doesn't expose the interface to select
193 // one, just go ahead without using profiles
194 return ret;
195 }
196
197 // assumption is that we should only have a small handful of selected
198 // profiles at a time (probably only 1), so calling each individually should
199 // not incur a large cost
200 for (const auto& objectPair : respData)
201 {
202 const std::string& path = objectPair.first;
203 for (const auto& ownerPair : objectPair.second)
204 {
205 const std::string& busName = ownerPair.first;
206 auto getProfile =
207 bus.new_method_call(busName.c_str(), path.c_str(),
208 "org.freedesktop.DBus.Properties", "Get");
209 getProfile.append(thermalControlIface, "Current");
210 std::variant<std::string> variantResp;
211 try
212 {
213 auto resp = bus.call(getProfile);
214 resp.read(variantResp);
215 }
216 catch (sdbusplus::exception_t&)
217 {
218 throw std::runtime_error("Failure getting profile");
219 }
220 std::string mode = std::get<std::string>(variantResp);
221 ret.emplace_back(std::move(mode));
222 }
223 }
224 if constexpr (DEBUG)
225 {
226 std::cout << "Profiles selected: ";
227 for (const auto& profile : ret)
228 {
229 std::cout << profile << " ";
230 }
231 std::cout << "\n";
232 }
233 return ret;
234}
235
James Feist1fe08952019-05-07 09:17:16 -0700236int eventHandler(sd_bus_message*, void* context, sd_bus_error*)
James Feist7136a5a2018-07-19 09:52:05 -0700237{
James Feist1fe08952019-05-07 09:17:16 -0700238
239 if (context == nullptr)
240 {
241 throw std::runtime_error("Invalid match");
242 }
243 boost::asio::steady_timer* timer =
244 static_cast<boost::asio::steady_timer*>(context);
245
246 // do a brief sleep as we tend to get a bunch of these events at
247 // once
248 timer->expires_after(std::chrono::seconds(2));
249 timer->async_wait([](const boost::system::error_code ec) {
250 if (ec == boost::asio::error::operation_aborted)
251 {
252 /* another timer started*/
253 return;
254 }
255
256 std::cout << "New configuration detected, reloading\n.";
Yong Li298a95c2020-04-07 15:11:02 +0800257 tryRestartControlLoops();
James Feist1fe08952019-05-07 09:17:16 -0700258 });
259
260 return 1;
261}
262
263void createMatches(sdbusplus::bus::bus& bus, boost::asio::steady_timer& timer)
264{
265 // this is a list because the matches can't be moved
266 static std::list<sdbusplus::bus::match::match> matches;
267
James Feist3987c8b2019-05-13 10:43:17 -0700268 const std::array<std::string, 4> interfaces = {
269 thermalControlIface, pidConfigurationInterface,
270 pidZoneConfigurationInterface, stepwiseConfigurationInterface};
James Feist1fe08952019-05-07 09:17:16 -0700271
272 // this list only needs to be created once
273 if (!matches.empty())
274 {
275 return;
276 }
277
278 // we restart when the configuration changes or there are new sensors
279 for (const auto& interface : interfaces)
280 {
281 matches.emplace_back(
282 bus,
283 "type='signal',member='PropertiesChanged',arg0namespace='" +
284 interface + "'",
285 eventHandler, &timer);
286 }
287 matches.emplace_back(
288 bus,
289 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
290 "sensors/'",
291 eventHandler, &timer);
292}
293
James Feist5ec20272019-07-10 11:59:57 -0700294void populatePidInfo(
295 sdbusplus::bus::bus& bus,
296 const std::unordered_map<std::string, DbusVariantType>& base,
297 struct conf::ControllerInfo& info, const std::string* thresholdProperty)
298{
299
300 info.type = std::get<std::string>(base.at("Class"));
301
302 if (info.type == "fan")
303 {
304 info.setpoint = 0;
305 }
306 else
307 {
308 info.setpoint =
309 std::visit(VariantToDoubleVisitor(), base.at("SetPoint"));
310 }
311
312 if (thresholdProperty != nullptr)
313 {
314 std::string interface;
315 if (*thresholdProperty == "WarningHigh" ||
316 *thresholdProperty == "WarningLow")
317 {
318 interface = thresholds::warningInterface;
319 }
320 else
321 {
322 interface = thresholds::criticalInterface;
323 }
324 const std::string& path = sensorConfig[info.inputs.front()].readPath;
325
326 DbusHelper helper;
327 std::string service = helper.getService(bus, interface, path);
328 double reading = 0;
329 try
330 {
331 helper.getProperty(bus, service, path, interface,
332 *thresholdProperty, reading);
333 }
334 catch (const sdbusplus::exception::SdBusError& ex)
335 {
336 // unsupported threshold, leaving reading at 0
337 }
338
339 info.setpoint += reading;
340 }
341
342 info.pidInfo.ts = 1.0; // currently unused
343 info.pidInfo.proportionalCoeff =
344 std::visit(VariantToDoubleVisitor(), base.at("PCoefficient"));
345 info.pidInfo.integralCoeff =
346 std::visit(VariantToDoubleVisitor(), base.at("ICoefficient"));
347 info.pidInfo.feedFwdOffset =
348 std::visit(VariantToDoubleVisitor(), base.at("FFOffCoefficient"));
349 info.pidInfo.feedFwdGain =
350 std::visit(VariantToDoubleVisitor(), base.at("FFGainCoefficient"));
351 info.pidInfo.integralLimit.max =
352 std::visit(VariantToDoubleVisitor(), base.at("ILimitMax"));
353 info.pidInfo.integralLimit.min =
354 std::visit(VariantToDoubleVisitor(), base.at("ILimitMin"));
355 info.pidInfo.outLim.max =
356 std::visit(VariantToDoubleVisitor(), base.at("OutLimitMax"));
357 info.pidInfo.outLim.min =
358 std::visit(VariantToDoubleVisitor(), base.at("OutLimitMin"));
359 info.pidInfo.slewNeg =
360 std::visit(VariantToDoubleVisitor(), base.at("SlewNeg"));
361 info.pidInfo.slewPos =
362 std::visit(VariantToDoubleVisitor(), base.at("SlewPos"));
363 double negativeHysteresis = 0;
364 double positiveHysteresis = 0;
365
366 auto findNeg = base.find("NegativeHysteresis");
367 auto findPos = base.find("PositiveHysteresis");
368
369 if (findNeg != base.end())
370 {
371 negativeHysteresis =
372 std::visit(VariantToDoubleVisitor(), findNeg->second);
373 }
374
375 if (findPos != base.end())
376 {
377 positiveHysteresis =
378 std::visit(VariantToDoubleVisitor(), findPos->second);
379 }
380
381 info.pidInfo.negativeHysteresis = negativeHysteresis;
382 info.pidInfo.positiveHysteresis = positiveHysteresis;
383}
384
James Feist1fe08952019-05-07 09:17:16 -0700385bool init(sdbusplus::bus::bus& bus, boost::asio::steady_timer& timer)
386{
387
388 sensorConfig.clear();
389 zoneConfig.clear();
390 zoneDetailsConfig.clear();
391
392 createMatches(bus, timer);
393
James Feist22c257a2018-08-31 14:07:12 -0700394 using DbusVariantType =
James Feist1f802f52019-02-08 13:51:43 -0800395 std::variant<uint64_t, int64_t, double, std::string,
396 std::vector<std::string>, std::vector<double>>;
James Feist22c257a2018-08-31 14:07:12 -0700397
James Feist7136a5a2018-07-19 09:52:05 -0700398 using ManagedObjectType = std::unordered_map<
399 sdbusplus::message::object_path,
James Feist22c257a2018-08-31 14:07:12 -0700400 std::unordered_map<std::string,
401 std::unordered_map<std::string, DbusVariantType>>>;
James Feist64f072a2018-08-10 16:39:24 -0700402
James Feist7136a5a2018-07-19 09:52:05 -0700403 auto mapper =
404 bus.new_method_call("xyz.openbmc_project.ObjectMapper",
405 "/xyz/openbmc_project/object_mapper",
406 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feist26e8c6a2018-10-25 10:38:26 -0700407 mapper.append("/", 0,
James Feist3987c8b2019-05-13 10:43:17 -0700408 std::array<const char*, 6>{objectManagerInterface,
409 pidConfigurationInterface,
410 pidZoneConfigurationInterface,
411 stepwiseConfigurationInterface,
412 sensorInterface, pwmInterface});
James Feist7136a5a2018-07-19 09:52:05 -0700413 std::unordered_map<
414 std::string, std::unordered_map<std::string, std::vector<std::string>>>
415 respData;
James Feist22c257a2018-08-31 14:07:12 -0700416 try
417 {
418 auto resp = bus.call(mapper);
James Feist22c257a2018-08-31 14:07:12 -0700419 resp.read(respData);
420 }
421 catch (sdbusplus::exception_t&)
422 {
423 // can't do anything without mapper call data
424 throw std::runtime_error("ObjectMapper Call Failure");
425 }
James Feist7136a5a2018-07-19 09:52:05 -0700426
James Feist7136a5a2018-07-19 09:52:05 -0700427 if (respData.empty())
428 {
James Feist22c257a2018-08-31 14:07:12 -0700429 // can't do anything without mapper call data
James Feist7136a5a2018-07-19 09:52:05 -0700430 throw std::runtime_error("No configuration data available from Mapper");
431 }
432 // create a map of pair of <has pid configuration, ObjectManager path>
433 std::unordered_map<std::string, std::pair<bool, std::string>> owners;
434 // and a map of <path, interface> for sensors
435 std::unordered_map<std::string, std::string> sensors;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700436 for (const auto& objectPair : respData)
James Feist7136a5a2018-07-19 09:52:05 -0700437 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700438 for (const auto& ownerPair : objectPair.second)
James Feist7136a5a2018-07-19 09:52:05 -0700439 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700440 auto& owner = owners[ownerPair.first];
441 for (const std::string& interface : ownerPair.second)
James Feist7136a5a2018-07-19 09:52:05 -0700442 {
443
444 if (interface == objectManagerInterface)
445 {
446 owner.second = objectPair.first;
447 }
448 if (interface == pidConfigurationInterface ||
James Feist22c257a2018-08-31 14:07:12 -0700449 interface == pidZoneConfigurationInterface ||
450 interface == stepwiseConfigurationInterface)
James Feist7136a5a2018-07-19 09:52:05 -0700451 {
452 owner.first = true;
453 }
454 if (interface == sensorInterface || interface == pwmInterface)
455 {
456 // we're not interested in pwm sensors, just pwm control
457 if (interface == sensorInterface &&
458 objectPair.first.find("pwm") != std::string::npos)
459 {
460 continue;
461 }
462 sensors[objectPair.first] = interface;
463 }
464 }
465 }
466 }
467 ManagedObjectType configurations;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700468 for (const auto& owner : owners)
James Feist7136a5a2018-07-19 09:52:05 -0700469 {
470 // skip if no pid configuration (means probably a sensor)
471 if (!owner.second.first)
472 {
473 continue;
474 }
475 auto endpoint = bus.new_method_call(
476 owner.first.c_str(), owner.second.second.c_str(),
477 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist22c257a2018-08-31 14:07:12 -0700478 ManagedObjectType configuration;
479 try
James Feist7136a5a2018-07-19 09:52:05 -0700480 {
James Feist22c257a2018-08-31 14:07:12 -0700481 auto responce = bus.call(endpoint);
James Feist22c257a2018-08-31 14:07:12 -0700482 responce.read(configuration);
483 }
484 catch (sdbusplus::exception_t&)
485 {
486 // this shouldn't happen, probably means daemon crashed
James Feist7136a5a2018-07-19 09:52:05 -0700487 throw std::runtime_error("Error getting managed objects from " +
488 owner.first);
489 }
James Feist22c257a2018-08-31 14:07:12 -0700490
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700491 for (auto& pathPair : configuration)
James Feist7136a5a2018-07-19 09:52:05 -0700492 {
493 if (pathPair.second.find(pidConfigurationInterface) !=
494 pathPair.second.end() ||
495 pathPair.second.find(pidZoneConfigurationInterface) !=
James Feist22c257a2018-08-31 14:07:12 -0700496 pathPair.second.end() ||
497 pathPair.second.find(stepwiseConfigurationInterface) !=
James Feist7136a5a2018-07-19 09:52:05 -0700498 pathPair.second.end())
499 {
500 configurations.emplace(pathPair);
501 }
James Feistf0096a02019-02-21 11:25:22 -0800502 }
503 }
504
505 // remove controllers from config that aren't in the current profile(s)
James Feist3987c8b2019-05-13 10:43:17 -0700506 std::vector<std::string> selectedProfiles = getSelectedProfiles(bus);
507 if (selectedProfiles.size())
James Feistf0096a02019-02-21 11:25:22 -0800508 {
James Feist3987c8b2019-05-13 10:43:17 -0700509 for (auto pathIt = configurations.begin();
510 pathIt != configurations.end();)
James Feistf0096a02019-02-21 11:25:22 -0800511 {
James Feist3987c8b2019-05-13 10:43:17 -0700512 for (auto confIt = pathIt->second.begin();
513 confIt != pathIt->second.end();)
James Feistf0096a02019-02-21 11:25:22 -0800514 {
James Feist3987c8b2019-05-13 10:43:17 -0700515 auto profilesFind = confIt->second.find("Profiles");
516 if (profilesFind == confIt->second.end())
James Feistf0096a02019-02-21 11:25:22 -0800517 {
James Feist3987c8b2019-05-13 10:43:17 -0700518 confIt++;
519 continue; // if no profiles selected, apply always
520 }
521 auto profiles =
522 std::get<std::vector<std::string>>(profilesFind->second);
523 if (profiles.empty())
524 {
525 confIt++;
526 continue;
527 }
528
529 bool found = false;
530 for (const std::string& profile : profiles)
531 {
532 if (std::find(selectedProfiles.begin(),
533 selectedProfiles.end(),
534 profile) != selectedProfiles.end())
535 {
536 found = true;
537 break;
538 }
539 }
540 if (found)
541 {
542 confIt++;
James Feistf0096a02019-02-21 11:25:22 -0800543 }
544 else
545 {
James Feist3987c8b2019-05-13 10:43:17 -0700546 confIt = pathIt->second.erase(confIt);
James Feistf0096a02019-02-21 11:25:22 -0800547 }
548 }
James Feist3987c8b2019-05-13 10:43:17 -0700549 if (pathIt->second.empty())
James Feistf0096a02019-02-21 11:25:22 -0800550 {
James Feist3987c8b2019-05-13 10:43:17 -0700551 pathIt = configurations.erase(pathIt);
James Feistf0096a02019-02-21 11:25:22 -0800552 }
James Feist3987c8b2019-05-13 10:43:17 -0700553 else
James Feistf0096a02019-02-21 11:25:22 -0800554 {
James Feist3987c8b2019-05-13 10:43:17 -0700555 pathIt++;
James Feistf0096a02019-02-21 11:25:22 -0800556 }
James Feist7136a5a2018-07-19 09:52:05 -0700557 }
558 }
James Feist8c3c51e2018-08-08 16:31:43 -0700559
560 // on dbus having an index field is a bit strange, so randomly
561 // assign index based on name property
James Feistffd418b2018-11-15 14:46:36 -0800562 std::vector<std::string> foundZones;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700563 for (const auto& configuration : configurations)
James Feist7136a5a2018-07-19 09:52:05 -0700564 {
565 auto findZone =
566 configuration.second.find(pidZoneConfigurationInterface);
567 if (findZone != configuration.second.end())
568 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700569 const auto& zone = findZone->second;
James Feistffd418b2018-11-15 14:46:36 -0800570
James Feist1f802f52019-02-08 13:51:43 -0800571 const std::string& name = std::get<std::string>(zone.at("Name"));
James Feistffd418b2018-11-15 14:46:36 -0800572 size_t index = getZoneIndex(name, foundZones);
James Feist8c3c51e2018-08-08 16:31:43 -0700573
Patrick Venturec54fbd82018-10-30 19:40:05 -0700574 auto& details = zoneDetailsConfig[index];
James Feist3484bed2019-02-25 13:28:18 -0800575 details.minThermalOutput = std::visit(VariantToDoubleVisitor(),
576 zone.at("MinThermalOutput"));
Patrick Venture8e2fdb32019-02-11 09:39:59 -0800577 details.failsafePercent = std::visit(VariantToDoubleVisitor(),
James Feist1f802f52019-02-08 13:51:43 -0800578 zone.at("FailSafePercent"));
James Feist7136a5a2018-07-19 09:52:05 -0700579 }
580 auto findBase = configuration.second.find(pidConfigurationInterface);
James Feist22c257a2018-08-31 14:07:12 -0700581 if (findBase != configuration.second.end())
James Feist7136a5a2018-07-19 09:52:05 -0700582 {
James Feist8c3c51e2018-08-08 16:31:43 -0700583
James Feist22c257a2018-08-31 14:07:12 -0700584 const auto& base =
585 configuration.second.at(pidConfigurationInterface);
586 const std::vector<std::string>& zones =
James Feist1f802f52019-02-08 13:51:43 -0800587 std::get<std::vector<std::string>>(base.at("Zones"));
James Feist22c257a2018-08-31 14:07:12 -0700588 for (const std::string& zone : zones)
James Feist7136a5a2018-07-19 09:52:05 -0700589 {
James Feistffd418b2018-11-15 14:46:36 -0800590 size_t index = getZoneIndex(zone, foundZones);
James Feistf81f2882019-02-26 11:26:36 -0800591 conf::PIDConf& conf = zoneConfig[index];
James Feist50fdfe32018-09-24 15:51:09 -0700592
593 std::vector<std::string> sensorNames =
James Feist1f802f52019-02-08 13:51:43 -0800594 std::get<std::vector<std::string>>(base.at("Inputs"));
James Feist50fdfe32018-09-24 15:51:09 -0700595 auto findOutputs =
596 base.find("Outputs"); // currently only fans have outputs
597 if (findOutputs != base.end())
598 {
599 std::vector<std::string> outputs =
James Feist1f802f52019-02-08 13:51:43 -0800600 std::get<std::vector<std::string>>(findOutputs->second);
James Feist50fdfe32018-09-24 15:51:09 -0700601 sensorNames.insert(sensorNames.end(), outputs.begin(),
602 outputs.end());
603 }
James Feist1738e2a2019-02-04 15:57:03 -0800604
James Feist50fdfe32018-09-24 15:51:09 -0700605 std::vector<std::string> inputs;
James Feist1738e2a2019-02-04 15:57:03 -0800606 std::vector<std::pair<std::string, std::string>>
607 sensorInterfaces;
James Feist50fdfe32018-09-24 15:51:09 -0700608 for (const std::string& sensorName : sensorNames)
609 {
610 std::string name = sensorName;
611 // replace spaces with underscores to be legal on dbus
612 std::replace(name.begin(), name.end(), ' ', '_');
James Feist1738e2a2019-02-04 15:57:03 -0800613 findSensors(sensors, name, sensorInterfaces);
614 }
James Feist50fdfe32018-09-24 15:51:09 -0700615
James Feist1738e2a2019-02-04 15:57:03 -0800616 for (const auto& sensorPathIfacePair : sensorInterfaces)
617 {
618
James Feist50fdfe32018-09-24 15:51:09 -0700619 if (sensorPathIfacePair.second == sensorInterface)
620 {
James Feist1738e2a2019-02-04 15:57:03 -0800621 size_t idx =
622 sensorPathIfacePair.first.find_last_of("/") + 1;
623 std::string shortName =
624 sensorPathIfacePair.first.substr(idx);
625
626 inputs.push_back(shortName);
627 auto& config = sensorConfig[shortName];
James Feist1f802f52019-02-08 13:51:43 -0800628 config.type = std::get<std::string>(base.at("Class"));
Patrick Venture69c51062019-02-11 09:46:03 -0800629 config.readPath = sensorPathIfacePair.first;
James Feist50fdfe32018-09-24 15:51:09 -0700630 // todo: maybe un-hardcode this if we run into slower
631 // timeouts with sensors
632 if (config.type == "temp")
633 {
James Feist2642cb52019-02-25 13:00:16 -0800634 config.timeout = 0;
James Feist3433cb62019-11-11 16:12:45 -0800635 config.ignoreDbusMinMax = true;
James Feist50fdfe32018-09-24 15:51:09 -0700636 }
637 }
638 else if (sensorPathIfacePair.second == pwmInterface)
639 {
640 // copy so we can modify it
641 for (std::string otherSensor : sensorNames)
642 {
James Feist1738e2a2019-02-04 15:57:03 -0800643 std::replace(otherSensor.begin(), otherSensor.end(),
644 ' ', '_');
645 if (sensorPathIfacePair.first.find(otherSensor) !=
646 std::string::npos)
James Feist50fdfe32018-09-24 15:51:09 -0700647 {
648 continue;
649 }
James Feist1738e2a2019-02-04 15:57:03 -0800650
Patrick Venturec54fbd82018-10-30 19:40:05 -0700651 auto& config = sensorConfig[otherSensor];
Patrick Venture69c51062019-02-11 09:46:03 -0800652 config.writePath = sensorPathIfacePair.first;
James Feist50fdfe32018-09-24 15:51:09 -0700653 // todo: un-hardcode this if there are fans with
654 // different ranges
655 config.max = 255;
656 config.min = 0;
657 }
658 }
659 }
James Feist1738e2a2019-02-04 15:57:03 -0800660
James Feist11d243d2019-06-24 16:18:40 -0700661 // if the sensors aren't available in the current state, don't
662 // add them to the configuration.
663 if (inputs.empty())
664 {
665 continue;
666 }
667
James Feist5ec20272019-07-10 11:59:57 -0700668 std::string offsetType;
James Feist50fdfe32018-09-24 15:51:09 -0700669
James Feist5ec20272019-07-10 11:59:57 -0700670 // SetPointOffset is a threshold value to pull from the sensor
671 // to apply an offset. For upper thresholds this means the
672 // setpoint is usually negative.
673 auto findSetpointOffset = base.find("SetPointOffset");
674 if (findSetpointOffset != base.end())
James Feist22c257a2018-08-31 14:07:12 -0700675 {
James Feist5ec20272019-07-10 11:59:57 -0700676 offsetType =
677 std::get<std::string>(findSetpointOffset->second);
678 if (std::find(thresholds::types.begin(),
679 thresholds::types.end(),
680 offsetType) == thresholds::types.end())
681 {
682 throw std::runtime_error("Unsupported type: " +
683 offsetType);
684 }
685 }
686
687 if (offsetType.empty())
688 {
689 struct conf::ControllerInfo& info =
690 conf[std::get<std::string>(base.at("Name"))];
691 info.inputs = std::move(inputs);
692 populatePidInfo(bus, base, info, nullptr);
James Feist22c257a2018-08-31 14:07:12 -0700693 }
694 else
695 {
James Feist5ec20272019-07-10 11:59:57 -0700696 // we have to split up the inputs, as in practice t-control
697 // values will differ, making setpoints differ
698 for (const std::string& input : inputs)
699 {
700 struct conf::ControllerInfo& info = conf[input];
701 info.inputs.emplace_back(input);
702 populatePidInfo(bus, base, info, &offsetType);
703 }
James Feist22c257a2018-08-31 14:07:12 -0700704 }
James Feist22c257a2018-08-31 14:07:12 -0700705 }
706 }
707 auto findStepwise =
708 configuration.second.find(stepwiseConfigurationInterface);
709 if (findStepwise != configuration.second.end())
710 {
711 const auto& base = findStepwise->second;
712 const std::vector<std::string>& zones =
James Feist1f802f52019-02-08 13:51:43 -0800713 std::get<std::vector<std::string>>(base.at("Zones"));
James Feist22c257a2018-08-31 14:07:12 -0700714 for (const std::string& zone : zones)
715 {
James Feistffd418b2018-11-15 14:46:36 -0800716 size_t index = getZoneIndex(zone, foundZones);
James Feistf81f2882019-02-26 11:26:36 -0800717 conf::PIDConf& conf = zoneConfig[index];
James Feist50fdfe32018-09-24 15:51:09 -0700718
719 std::vector<std::string> inputs;
720 std::vector<std::string> sensorNames =
James Feist1f802f52019-02-08 13:51:43 -0800721 std::get<std::vector<std::string>>(base.at("Inputs"));
James Feist50fdfe32018-09-24 15:51:09 -0700722
James Feist1738e2a2019-02-04 15:57:03 -0800723 bool sensorFound = false;
James Feist50fdfe32018-09-24 15:51:09 -0700724 for (const std::string& sensorName : sensorNames)
725 {
726 std::string name = sensorName;
727 // replace spaces with underscores to be legal on dbus
728 std::replace(name.begin(), name.end(), ' ', '_');
James Feist1738e2a2019-02-04 15:57:03 -0800729 std::vector<std::pair<std::string, std::string>>
730 sensorPathIfacePairs;
James Feist50fdfe32018-09-24 15:51:09 -0700731
James Feist1738e2a2019-02-04 15:57:03 -0800732 if (!findSensors(sensors, name, sensorPathIfacePairs))
James Feist50fdfe32018-09-24 15:51:09 -0700733 {
James Feist50fdfe32018-09-24 15:51:09 -0700734 break;
735 }
736
James Feist1738e2a2019-02-04 15:57:03 -0800737 for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
738 {
739 size_t idx =
740 sensorPathIfacePair.first.find_last_of("/") + 1;
741 std::string shortName =
742 sensorPathIfacePair.first.substr(idx);
James Feist50fdfe32018-09-24 15:51:09 -0700743
James Feist1738e2a2019-02-04 15:57:03 -0800744 inputs.push_back(shortName);
745 auto& config = sensorConfig[shortName];
Patrick Venture69c51062019-02-11 09:46:03 -0800746 config.readPath = sensorPathIfacePair.first;
James Feist1738e2a2019-02-04 15:57:03 -0800747 config.type = "temp";
James Feist3660b382019-11-11 16:29:19 -0800748 config.ignoreDbusMinMax = true;
James Feist1738e2a2019-02-04 15:57:03 -0800749 // todo: maybe un-hardcode this if we run into slower
750 // timeouts with sensors
751
James Feist2642cb52019-02-25 13:00:16 -0800752 config.timeout = 0;
James Feist1738e2a2019-02-04 15:57:03 -0800753 sensorFound = true;
754 }
James Feist50fdfe32018-09-24 15:51:09 -0700755 }
756 if (!sensorFound)
757 {
758 continue;
759 }
James Feistf81f2882019-02-26 11:26:36 -0800760 struct conf::ControllerInfo& info =
James Feist1f802f52019-02-08 13:51:43 -0800761 conf[std::get<std::string>(base.at("Name"))];
James Feist50fdfe32018-09-24 15:51:09 -0700762 info.inputs = std::move(inputs);
763
James Feist22c257a2018-08-31 14:07:12 -0700764 info.type = "stepwise";
765 info.stepwiseInfo.ts = 1.0; // currently unused
James Feist3dfaafd2018-09-20 15:46:58 -0700766 info.stepwiseInfo.positiveHysteresis = 0.0;
767 info.stepwiseInfo.negativeHysteresis = 0.0;
James Feist608304d2019-02-25 10:01:42 -0800768 std::string subtype = std::get<std::string>(base.at("Class"));
769
770 info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
James Feist3dfaafd2018-09-20 15:46:58 -0700771 auto findPosHyst = base.find("PositiveHysteresis");
772 auto findNegHyst = base.find("NegativeHysteresis");
773 if (findPosHyst != base.end())
774 {
James Feist1f802f52019-02-08 13:51:43 -0800775 info.stepwiseInfo.positiveHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -0800776 VariantToDoubleVisitor(), findPosHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -0700777 }
778 if (findNegHyst != base.end())
779 {
James Feist5782ab82019-04-02 08:38:48 -0700780 info.stepwiseInfo.negativeHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -0800781 VariantToDoubleVisitor(), findNegHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -0700782 }
James Feist22c257a2018-08-31 14:07:12 -0700783 std::vector<double> readings =
James Feist1f802f52019-02-08 13:51:43 -0800784 std::get<std::vector<double>>(base.at("Reading"));
James Feist22c257a2018-08-31 14:07:12 -0700785 if (readings.size() > ec::maxStepwisePoints)
786 {
787 throw std::invalid_argument("Too many stepwise points.");
788 }
789 if (readings.empty())
790 {
791 throw std::invalid_argument(
792 "Must have one stepwise point.");
793 }
794 std::copy(readings.begin(), readings.end(),
795 info.stepwiseInfo.reading);
796 if (readings.size() < ec::maxStepwisePoints)
797 {
798 info.stepwiseInfo.reading[readings.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800799 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -0700800 }
801 std::vector<double> outputs =
James Feist1f802f52019-02-08 13:51:43 -0800802 std::get<std::vector<double>>(base.at("Output"));
James Feist22c257a2018-08-31 14:07:12 -0700803 if (readings.size() != outputs.size())
804 {
805 throw std::invalid_argument(
806 "Outputs size must match readings");
807 }
808 std::copy(outputs.begin(), outputs.end(),
809 info.stepwiseInfo.output);
810 if (outputs.size() < ec::maxStepwisePoints)
811 {
812 info.stepwiseInfo.output[outputs.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800813 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -0700814 }
James Feist7136a5a2018-07-19 09:52:05 -0700815 }
816 }
817 }
James Feistf0096a02019-02-21 11:25:22 -0800818 if constexpr (DEBUG)
James Feist7136a5a2018-07-19 09:52:05 -0700819 {
820 debugPrint();
821 }
James Feistc959c422018-11-01 12:33:40 -0700822 if (zoneConfig.empty() || zoneDetailsConfig.empty())
James Feist50fdfe32018-09-24 15:51:09 -0700823 {
James Feist1fe08952019-05-07 09:17:16 -0700824 std::cerr
825 << "No fan zones, application pausing until new configuration\n";
826 return false;
James Feist50fdfe32018-09-24 15:51:09 -0700827 }
James Feist1fe08952019-05-07 09:17:16 -0700828 return true;
James Feist7136a5a2018-07-19 09:52:05 -0700829}
830} // namespace dbus_configuration