blob: 870e5e8d37339349114db7fcc3104a466c2fdf47 [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* fanProfileConfigurationIface =
49 "xyz.openbmc_project.Configuration.FanProfile";
50constexpr 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";
53constexpr const char* pwmInterface = "xyz.openbmc_project.Control.FanPwm";
James Feist7136a5a2018-07-19 09:52:05 -070054
55namespace dbus_configuration
56{
57
James Feist1738e2a2019-02-04 15:57:03 -080058bool findSensors(const std::unordered_map<std::string, std::string>& sensors,
59 const std::string& search,
60 std::vector<std::pair<std::string, std::string>>& matches)
James Feist7136a5a2018-07-19 09:52:05 -070061{
James Feist1738e2a2019-02-04 15:57:03 -080062 std::smatch match;
63 std::regex reg(search);
64 for (const auto& sensor : sensors)
James Feist7136a5a2018-07-19 09:52:05 -070065 {
James Feist1738e2a2019-02-04 15:57:03 -080066 if (std::regex_search(sensor.first, match, reg))
67 {
68 matches.push_back(sensor);
69 }
James Feist7136a5a2018-07-19 09:52:05 -070070 }
Patrick Venture107a25d2018-10-13 14:08:09 -070071
James Feist1738e2a2019-02-04 15:57:03 -080072 return matches.size() > 0;
James Feist7136a5a2018-07-19 09:52:05 -070073}
74
75// this function prints the configuration into a form similar to the cpp
76// generated code to help in verification, should be turned off during normal
77// use
78void debugPrint(void)
79{
80 // print sensor config
81 std::cout << "sensor config:\n";
82 std::cout << "{\n";
Patrick Venturec54fbd82018-10-30 19:40:05 -070083 for (const auto& pair : sensorConfig)
James Feist7136a5a2018-07-19 09:52:05 -070084 {
85
86 std::cout << "\t{" << pair.first << ",\n\t\t{";
87 std::cout << pair.second.type << ", ";
Patrick Venture69c51062019-02-11 09:46:03 -080088 std::cout << pair.second.readPath << ", ";
89 std::cout << pair.second.writePath << ", ";
James Feist7136a5a2018-07-19 09:52:05 -070090 std::cout << pair.second.min << ", ";
91 std::cout << pair.second.max << ", ";
92 std::cout << pair.second.timeout << "},\n\t},\n";
93 }
94 std::cout << "}\n\n";
95 std::cout << "ZoneDetailsConfig\n";
96 std::cout << "{\n";
Patrick Venturec54fbd82018-10-30 19:40:05 -070097 for (const auto& zone : zoneDetailsConfig)
James Feist7136a5a2018-07-19 09:52:05 -070098 {
99 std::cout << "\t{" << zone.first << ",\n";
James Feist3484bed2019-02-25 13:28:18 -0800100 std::cout << "\t\t{" << zone.second.minThermalOutput << ", ";
Patrick Venture8e2fdb32019-02-11 09:39:59 -0800101 std::cout << zone.second.failsafePercent << "}\n\t},\n";
James Feist7136a5a2018-07-19 09:52:05 -0700102 }
103 std::cout << "}\n\n";
104 std::cout << "ZoneConfig\n";
105 std::cout << "{\n";
Patrick Venturec54fbd82018-10-30 19:40:05 -0700106 for (const auto& zone : zoneConfig)
James Feist7136a5a2018-07-19 09:52:05 -0700107 {
108 std::cout << "\t{" << zone.first << "\n";
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700109 for (const auto& pidconf : zone.second)
James Feist7136a5a2018-07-19 09:52:05 -0700110 {
111 std::cout << "\t\t{" << pidconf.first << ",\n";
112 std::cout << "\t\t\t{" << pidconf.second.type << ",\n";
113 std::cout << "\t\t\t{";
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700114 for (const auto& input : pidconf.second.inputs)
James Feist7136a5a2018-07-19 09:52:05 -0700115 {
116 std::cout << "\n\t\t\t" << input << ",\n";
117 }
118 std::cout << "\t\t\t}\n";
119 std::cout << "\t\t\t" << pidconf.second.setpoint << ",\n";
James Feist22c257a2018-08-31 14:07:12 -0700120 std::cout << "\t\t\t{" << pidconf.second.pidInfo.ts << ",\n";
Patrick Venture7442c372019-02-11 10:21:05 -0800121 std::cout << "\t\t\t" << pidconf.second.pidInfo.proportionalCoeff
122 << ",\n";
123 std::cout << "\t\t\t" << pidconf.second.pidInfo.integralCoeff
124 << ",\n";
125 std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdOffset
126 << ",\n";
127 std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdGain
128 << ",\n";
129 std::cout << "\t\t\t{" << pidconf.second.pidInfo.integralLimit.min
130 << "," << pidconf.second.pidInfo.integralLimit.max
131 << "},\n";
132 std::cout << "\t\t\t{" << pidconf.second.pidInfo.outLim.min << ","
133 << pidconf.second.pidInfo.outLim.max << "},\n";
134 std::cout << "\t\t\t" << pidconf.second.pidInfo.slewNeg << ",\n";
135 std::cout << "\t\t\t" << pidconf.second.pidInfo.slewPos << ",\n";
James Feist7136a5a2018-07-19 09:52:05 -0700136 std::cout << "\t\t\t}\n\t\t}\n";
137 }
138 std::cout << "\t},\n";
139 }
140 std::cout << "}\n\n";
141}
142
James Feistffd418b2018-11-15 14:46:36 -0800143size_t getZoneIndex(const std::string& name, std::vector<std::string>& zones)
144{
145 auto it = std::find(zones.begin(), zones.end(), name);
146 if (it == zones.end())
147 {
148 zones.emplace_back(name);
149 it = zones.end() - 1;
150 }
151
152 return it - zones.begin();
153}
154
James Feistf0096a02019-02-21 11:25:22 -0800155std::vector<std::string> getSelectedProfiles(sdbusplus::bus::bus& bus)
156{
157 std::vector<std::string> ret;
158 auto mapper =
159 bus.new_method_call("xyz.openbmc_project.ObjectMapper",
160 "/xyz/openbmc_project/object_mapper",
161 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
162 mapper.append("/", 0, std::array<const char*, 1>{thermalControlIface});
163 std::unordered_map<
164 std::string, std::unordered_map<std::string, std::vector<std::string>>>
165 respData;
166
167 try
168 {
169 auto resp = bus.call(mapper);
170 resp.read(respData);
171 }
172 catch (sdbusplus::exception_t&)
173 {
174 // can't do anything without mapper call data
175 throw std::runtime_error("ObjectMapper Call Failure");
176 }
177 if (respData.empty())
178 {
179 // if the user has profiles but doesn't expose the interface to select
180 // one, just go ahead without using profiles
181 return ret;
182 }
183
184 // assumption is that we should only have a small handful of selected
185 // profiles at a time (probably only 1), so calling each individually should
186 // not incur a large cost
187 for (const auto& objectPair : respData)
188 {
189 const std::string& path = objectPair.first;
190 for (const auto& ownerPair : objectPair.second)
191 {
192 const std::string& busName = ownerPair.first;
193 auto getProfile =
194 bus.new_method_call(busName.c_str(), path.c_str(),
195 "org.freedesktop.DBus.Properties", "Get");
196 getProfile.append(thermalControlIface, "Current");
197 std::variant<std::string> variantResp;
198 try
199 {
200 auto resp = bus.call(getProfile);
201 resp.read(variantResp);
202 }
203 catch (sdbusplus::exception_t&)
204 {
205 throw std::runtime_error("Failure getting profile");
206 }
207 std::string mode = std::get<std::string>(variantResp);
208 ret.emplace_back(std::move(mode));
209 }
210 }
211 if constexpr (DEBUG)
212 {
213 std::cout << "Profiles selected: ";
214 for (const auto& profile : ret)
215 {
216 std::cout << profile << " ";
217 }
218 std::cout << "\n";
219 }
220 return ret;
221}
222
James Feist1fe08952019-05-07 09:17:16 -0700223int eventHandler(sd_bus_message*, void* context, sd_bus_error*)
James Feist7136a5a2018-07-19 09:52:05 -0700224{
James Feist1fe08952019-05-07 09:17:16 -0700225
226 if (context == nullptr)
227 {
228 throw std::runtime_error("Invalid match");
229 }
230 boost::asio::steady_timer* timer =
231 static_cast<boost::asio::steady_timer*>(context);
232
233 // do a brief sleep as we tend to get a bunch of these events at
234 // once
235 timer->expires_after(std::chrono::seconds(2));
236 timer->async_wait([](const boost::system::error_code ec) {
237 if (ec == boost::asio::error::operation_aborted)
238 {
239 /* another timer started*/
240 return;
241 }
242
243 std::cout << "New configuration detected, reloading\n.";
244 restartControlLoops();
245 });
246
247 return 1;
248}
249
250void createMatches(sdbusplus::bus::bus& bus, boost::asio::steady_timer& timer)
251{
252 // this is a list because the matches can't be moved
253 static std::list<sdbusplus::bus::match::match> matches;
254
255 const std::array<std::string, 5> interfaces = {
256 thermalControlIface, fanProfileConfigurationIface,
257 pidConfigurationInterface, pidZoneConfigurationInterface,
258 stepwiseConfigurationInterface};
259
260 // this list only needs to be created once
261 if (!matches.empty())
262 {
263 return;
264 }
265
266 // we restart when the configuration changes or there are new sensors
267 for (const auto& interface : interfaces)
268 {
269 matches.emplace_back(
270 bus,
271 "type='signal',member='PropertiesChanged',arg0namespace='" +
272 interface + "'",
273 eventHandler, &timer);
274 }
275 matches.emplace_back(
276 bus,
277 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
278 "sensors/'",
279 eventHandler, &timer);
280}
281
282bool init(sdbusplus::bus::bus& bus, boost::asio::steady_timer& timer)
283{
284
285 sensorConfig.clear();
286 zoneConfig.clear();
287 zoneDetailsConfig.clear();
288
289 createMatches(bus, timer);
290
James Feist22c257a2018-08-31 14:07:12 -0700291 using DbusVariantType =
James Feist1f802f52019-02-08 13:51:43 -0800292 std::variant<uint64_t, int64_t, double, std::string,
293 std::vector<std::string>, std::vector<double>>;
James Feist22c257a2018-08-31 14:07:12 -0700294
James Feist7136a5a2018-07-19 09:52:05 -0700295 using ManagedObjectType = std::unordered_map<
296 sdbusplus::message::object_path,
James Feist22c257a2018-08-31 14:07:12 -0700297 std::unordered_map<std::string,
298 std::unordered_map<std::string, DbusVariantType>>>;
James Feist64f072a2018-08-10 16:39:24 -0700299
James Feist7136a5a2018-07-19 09:52:05 -0700300 auto mapper =
301 bus.new_method_call("xyz.openbmc_project.ObjectMapper",
302 "/xyz/openbmc_project/object_mapper",
303 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feist26e8c6a2018-10-25 10:38:26 -0700304 mapper.append("/", 0,
James Feistf0096a02019-02-21 11:25:22 -0800305 std::array<const char*, 7>{
306 objectManagerInterface, pidConfigurationInterface,
307 pidZoneConfigurationInterface,
308 stepwiseConfigurationInterface, sensorInterface,
309 pwmInterface, fanProfileConfigurationIface});
James Feist7136a5a2018-07-19 09:52:05 -0700310 std::unordered_map<
311 std::string, std::unordered_map<std::string, std::vector<std::string>>>
312 respData;
James Feist22c257a2018-08-31 14:07:12 -0700313 try
314 {
315 auto resp = bus.call(mapper);
James Feist22c257a2018-08-31 14:07:12 -0700316 resp.read(respData);
317 }
318 catch (sdbusplus::exception_t&)
319 {
320 // can't do anything without mapper call data
321 throw std::runtime_error("ObjectMapper Call Failure");
322 }
James Feist7136a5a2018-07-19 09:52:05 -0700323
James Feist7136a5a2018-07-19 09:52:05 -0700324 if (respData.empty())
325 {
James Feist22c257a2018-08-31 14:07:12 -0700326 // can't do anything without mapper call data
James Feist7136a5a2018-07-19 09:52:05 -0700327 throw std::runtime_error("No configuration data available from Mapper");
328 }
329 // create a map of pair of <has pid configuration, ObjectManager path>
330 std::unordered_map<std::string, std::pair<bool, std::string>> owners;
331 // and a map of <path, interface> for sensors
332 std::unordered_map<std::string, std::string> sensors;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700333 for (const auto& objectPair : respData)
James Feist7136a5a2018-07-19 09:52:05 -0700334 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700335 for (const auto& ownerPair : objectPair.second)
James Feist7136a5a2018-07-19 09:52:05 -0700336 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700337 auto& owner = owners[ownerPair.first];
338 for (const std::string& interface : ownerPair.second)
James Feist7136a5a2018-07-19 09:52:05 -0700339 {
340
341 if (interface == objectManagerInterface)
342 {
343 owner.second = objectPair.first;
344 }
345 if (interface == pidConfigurationInterface ||
James Feist22c257a2018-08-31 14:07:12 -0700346 interface == pidZoneConfigurationInterface ||
347 interface == stepwiseConfigurationInterface)
James Feist7136a5a2018-07-19 09:52:05 -0700348 {
349 owner.first = true;
350 }
351 if (interface == sensorInterface || interface == pwmInterface)
352 {
353 // we're not interested in pwm sensors, just pwm control
354 if (interface == sensorInterface &&
355 objectPair.first.find("pwm") != std::string::npos)
356 {
357 continue;
358 }
359 sensors[objectPair.first] = interface;
360 }
361 }
362 }
363 }
364 ManagedObjectType configurations;
James Feistf0096a02019-02-21 11:25:22 -0800365 ManagedObjectType profiles;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700366 for (const auto& owner : owners)
James Feist7136a5a2018-07-19 09:52:05 -0700367 {
368 // skip if no pid configuration (means probably a sensor)
369 if (!owner.second.first)
370 {
371 continue;
372 }
373 auto endpoint = bus.new_method_call(
374 owner.first.c_str(), owner.second.second.c_str(),
375 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist22c257a2018-08-31 14:07:12 -0700376 ManagedObjectType configuration;
377 try
James Feist7136a5a2018-07-19 09:52:05 -0700378 {
James Feist22c257a2018-08-31 14:07:12 -0700379 auto responce = bus.call(endpoint);
James Feist22c257a2018-08-31 14:07:12 -0700380 responce.read(configuration);
381 }
382 catch (sdbusplus::exception_t&)
383 {
384 // this shouldn't happen, probably means daemon crashed
James Feist7136a5a2018-07-19 09:52:05 -0700385 throw std::runtime_error("Error getting managed objects from " +
386 owner.first);
387 }
James Feist22c257a2018-08-31 14:07:12 -0700388
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700389 for (auto& pathPair : configuration)
James Feist7136a5a2018-07-19 09:52:05 -0700390 {
391 if (pathPair.second.find(pidConfigurationInterface) !=
392 pathPair.second.end() ||
393 pathPair.second.find(pidZoneConfigurationInterface) !=
James Feist22c257a2018-08-31 14:07:12 -0700394 pathPair.second.end() ||
395 pathPair.second.find(stepwiseConfigurationInterface) !=
James Feist7136a5a2018-07-19 09:52:05 -0700396 pathPair.second.end())
397 {
398 configurations.emplace(pathPair);
399 }
James Feistf0096a02019-02-21 11:25:22 -0800400 if (pathPair.second.find(fanProfileConfigurationIface) !=
401 pathPair.second.end())
402 {
403 profiles.emplace(pathPair);
404 }
405 }
406 }
407
408 // remove controllers from config that aren't in the current profile(s)
409 if (profiles.size())
410 {
411 std::vector<std::string> selectedProfiles = getSelectedProfiles(bus);
412 if (selectedProfiles.size())
413 {
414 // make the names match the dbus name
415 for (auto& profile : selectedProfiles)
416 {
417 std::replace(profile.begin(), profile.end(), ' ', '_');
418 }
419
420 // remove profiles that aren't supported
421 for (auto it = profiles.begin(); it != profiles.end();)
422 {
423 auto& path = it->first.str;
424 auto inConfig = std::find_if(
425 selectedProfiles.begin(), selectedProfiles.end(),
426 [&path](const std::string& key) {
427 return (path.find(key) != std::string::npos);
428 });
429 if (inConfig == selectedProfiles.end())
430 {
431 it = profiles.erase(it);
432 }
433 else
434 {
435 it++;
436 }
437 }
438 std::vector<std::string> allowedControllers;
439
440 // create a vector of profile match strings
441 for (const auto& profile : profiles)
442 {
443 const auto& interface =
444 profile.second.at(fanProfileConfigurationIface);
445 auto findController = interface.find("Controllers");
446 if (findController == interface.end())
447 {
448 throw std::runtime_error("Profile Missing Controllers");
449 }
450 std::vector<std::string> controllers =
451 std::get<std::vector<std::string>>(findController->second);
452 allowedControllers.insert(allowedControllers.end(),
453 controllers.begin(),
454 controllers.end());
455 }
456 std::vector<std::regex> regexes;
457 for (auto& controller : allowedControllers)
458 {
459 std::replace(controller.begin(), controller.end(), ' ', '_');
460 try
461 {
462 regexes.push_back(std::regex(controller));
463 }
464 catch (std::regex_error&)
465 {
466 std::cerr << "Invalid regex: " << controller << "\n";
467 throw;
468 }
469 }
470
471 // remove configurations that don't match any of the regexes
472 for (auto it = configurations.begin(); it != configurations.end();)
473 {
474 const std::string& path = it->first;
475 size_t lastSlash = path.rfind("/");
476 if (lastSlash == std::string::npos)
477 {
478 // if this happens, the mapper has a bug
479 throw std::runtime_error("Invalid path in configuration");
480 }
481 std::string name = path.substr(lastSlash);
482 auto allowed = std::find_if(
483 regexes.begin(), regexes.end(), [&name](auto& reg) {
484 std::smatch match;
485 return std::regex_search(name, match, reg);
486 });
487 if (allowed == regexes.end())
488 {
489 auto findZone =
490 it->second.find(pidZoneConfigurationInterface);
491
492 // if there is a fanzone under the given configuration, keep
493 // it but remove any of the other controllers
494 if (findZone != it->second.end())
495 {
496 for (auto subIt = it->second.begin();
497 subIt != it->second.end();)
498 {
499 if (subIt == findZone)
500 {
501 subIt++;
502 }
503 else
504 {
505 subIt = it->second.erase(subIt);
506 }
507 }
508 it++;
509 }
510 else
511 {
512 it = configurations.erase(it);
513 }
514 }
515 else
516 {
517 it++;
518 }
519 }
James Feist7136a5a2018-07-19 09:52:05 -0700520 }
521 }
James Feist8c3c51e2018-08-08 16:31:43 -0700522
523 // on dbus having an index field is a bit strange, so randomly
524 // assign index based on name property
James Feistffd418b2018-11-15 14:46:36 -0800525 std::vector<std::string> foundZones;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700526 for (const auto& configuration : configurations)
James Feist7136a5a2018-07-19 09:52:05 -0700527 {
528 auto findZone =
529 configuration.second.find(pidZoneConfigurationInterface);
530 if (findZone != configuration.second.end())
531 {
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700532 const auto& zone = findZone->second;
James Feistffd418b2018-11-15 14:46:36 -0800533
James Feist1f802f52019-02-08 13:51:43 -0800534 const std::string& name = std::get<std::string>(zone.at("Name"));
James Feistffd418b2018-11-15 14:46:36 -0800535 size_t index = getZoneIndex(name, foundZones);
James Feist8c3c51e2018-08-08 16:31:43 -0700536
Patrick Venturec54fbd82018-10-30 19:40:05 -0700537 auto& details = zoneDetailsConfig[index];
James Feist3484bed2019-02-25 13:28:18 -0800538 details.minThermalOutput = std::visit(VariantToDoubleVisitor(),
539 zone.at("MinThermalOutput"));
Patrick Venture8e2fdb32019-02-11 09:39:59 -0800540 details.failsafePercent = std::visit(VariantToDoubleVisitor(),
James Feist1f802f52019-02-08 13:51:43 -0800541 zone.at("FailSafePercent"));
James Feist7136a5a2018-07-19 09:52:05 -0700542 }
543 auto findBase = configuration.second.find(pidConfigurationInterface);
James Feist22c257a2018-08-31 14:07:12 -0700544 if (findBase != configuration.second.end())
James Feist7136a5a2018-07-19 09:52:05 -0700545 {
James Feist8c3c51e2018-08-08 16:31:43 -0700546
James Feist22c257a2018-08-31 14:07:12 -0700547 const auto& base =
548 configuration.second.at(pidConfigurationInterface);
549 const std::vector<std::string>& zones =
James Feist1f802f52019-02-08 13:51:43 -0800550 std::get<std::vector<std::string>>(base.at("Zones"));
James Feist22c257a2018-08-31 14:07:12 -0700551 for (const std::string& zone : zones)
James Feist7136a5a2018-07-19 09:52:05 -0700552 {
James Feistffd418b2018-11-15 14:46:36 -0800553 size_t index = getZoneIndex(zone, foundZones);
James Feistf81f2882019-02-26 11:26:36 -0800554 conf::PIDConf& conf = zoneConfig[index];
James Feist50fdfe32018-09-24 15:51:09 -0700555
556 std::vector<std::string> sensorNames =
James Feist1f802f52019-02-08 13:51:43 -0800557 std::get<std::vector<std::string>>(base.at("Inputs"));
James Feist50fdfe32018-09-24 15:51:09 -0700558 auto findOutputs =
559 base.find("Outputs"); // currently only fans have outputs
560 if (findOutputs != base.end())
561 {
562 std::vector<std::string> outputs =
James Feist1f802f52019-02-08 13:51:43 -0800563 std::get<std::vector<std::string>>(findOutputs->second);
James Feist50fdfe32018-09-24 15:51:09 -0700564 sensorNames.insert(sensorNames.end(), outputs.begin(),
565 outputs.end());
566 }
James Feist1738e2a2019-02-04 15:57:03 -0800567
James Feist50fdfe32018-09-24 15:51:09 -0700568 std::vector<std::string> inputs;
James Feist1738e2a2019-02-04 15:57:03 -0800569 std::vector<std::pair<std::string, std::string>>
570 sensorInterfaces;
James Feist50fdfe32018-09-24 15:51:09 -0700571 for (const std::string& sensorName : sensorNames)
572 {
573 std::string name = sensorName;
574 // replace spaces with underscores to be legal on dbus
575 std::replace(name.begin(), name.end(), ' ', '_');
James Feist1738e2a2019-02-04 15:57:03 -0800576 findSensors(sensors, name, sensorInterfaces);
577 }
James Feist50fdfe32018-09-24 15:51:09 -0700578
James Feist1738e2a2019-02-04 15:57:03 -0800579 // if the sensors aren't available in the current state, don't
580 // add them to the configuration.
581 if (sensorInterfaces.empty())
582 {
583 continue;
584 }
585 for (const auto& sensorPathIfacePair : sensorInterfaces)
586 {
587
James Feist50fdfe32018-09-24 15:51:09 -0700588 if (sensorPathIfacePair.second == sensorInterface)
589 {
James Feist1738e2a2019-02-04 15:57:03 -0800590 size_t idx =
591 sensorPathIfacePair.first.find_last_of("/") + 1;
592 std::string shortName =
593 sensorPathIfacePair.first.substr(idx);
594
595 inputs.push_back(shortName);
596 auto& config = sensorConfig[shortName];
James Feist1f802f52019-02-08 13:51:43 -0800597 config.type = std::get<std::string>(base.at("Class"));
Patrick Venture69c51062019-02-11 09:46:03 -0800598 config.readPath = sensorPathIfacePair.first;
James Feist50fdfe32018-09-24 15:51:09 -0700599 // todo: maybe un-hardcode this if we run into slower
600 // timeouts with sensors
601 if (config.type == "temp")
602 {
James Feist2642cb52019-02-25 13:00:16 -0800603 config.timeout = 0;
James Feist50fdfe32018-09-24 15:51:09 -0700604 }
James Feist75eb7692019-02-25 12:50:02 -0800605 else if (config.type == "fan")
606 {
607 config.max = conf::inheritValueFromDbus;
608 config.min = conf::inheritValueFromDbus;
609 }
James Feist50fdfe32018-09-24 15:51:09 -0700610 }
611 else if (sensorPathIfacePair.second == pwmInterface)
612 {
613 // copy so we can modify it
614 for (std::string otherSensor : sensorNames)
615 {
James Feist1738e2a2019-02-04 15:57:03 -0800616 std::replace(otherSensor.begin(), otherSensor.end(),
617 ' ', '_');
618 if (sensorPathIfacePair.first.find(otherSensor) !=
619 std::string::npos)
James Feist50fdfe32018-09-24 15:51:09 -0700620 {
621 continue;
622 }
James Feist1738e2a2019-02-04 15:57:03 -0800623
Patrick Venturec54fbd82018-10-30 19:40:05 -0700624 auto& config = sensorConfig[otherSensor];
Patrick Venture69c51062019-02-11 09:46:03 -0800625 config.writePath = sensorPathIfacePair.first;
James Feist50fdfe32018-09-24 15:51:09 -0700626 // todo: un-hardcode this if there are fans with
627 // different ranges
628 config.max = 255;
629 config.min = 0;
630 }
631 }
632 }
James Feist1738e2a2019-02-04 15:57:03 -0800633
James Feistf81f2882019-02-26 11:26:36 -0800634 struct conf::ControllerInfo& info =
James Feist1f802f52019-02-08 13:51:43 -0800635 conf[std::get<std::string>(base.at("Name"))];
James Feist50fdfe32018-09-24 15:51:09 -0700636 info.inputs = std::move(inputs);
637
James Feist1f802f52019-02-08 13:51:43 -0800638 info.type = std::get<std::string>(base.at("Class"));
James Feist22c257a2018-08-31 14:07:12 -0700639 // todo: auto generation yaml -> c script seems to discard this
640 // value for fans, verify this is okay
641 if (info.type == "fan")
642 {
643 info.setpoint = 0;
644 }
645 else
646 {
James Feist1f802f52019-02-08 13:51:43 -0800647 info.setpoint = std::visit(VariantToDoubleVisitor(),
648 base.at("SetPoint"));
James Feist22c257a2018-08-31 14:07:12 -0700649 }
650 info.pidInfo.ts = 1.0; // currently unused
Patrick Venture7442c372019-02-11 10:21:05 -0800651 info.pidInfo.proportionalCoeff = std::visit(
652 VariantToDoubleVisitor(), base.at("PCoefficient"));
653 info.pidInfo.integralCoeff = std::visit(
654 VariantToDoubleVisitor(), base.at("ICoefficient"));
655 info.pidInfo.feedFwdOffset = std::visit(
656 VariantToDoubleVisitor(), base.at("FFOffCoefficient"));
657 info.pidInfo.feedFwdGain = std::visit(
658 VariantToDoubleVisitor(), base.at("FFGainCoefficient"));
659 info.pidInfo.integralLimit.max =
James Feist1f802f52019-02-08 13:51:43 -0800660 std::visit(VariantToDoubleVisitor(), base.at("ILimitMax"));
Patrick Venture7442c372019-02-11 10:21:05 -0800661 info.pidInfo.integralLimit.min =
James Feist1f802f52019-02-08 13:51:43 -0800662 std::visit(VariantToDoubleVisitor(), base.at("ILimitMin"));
Patrick Venture7442c372019-02-11 10:21:05 -0800663 info.pidInfo.outLim.max = std::visit(VariantToDoubleVisitor(),
664 base.at("OutLimitMax"));
665 info.pidInfo.outLim.min = std::visit(VariantToDoubleVisitor(),
666 base.at("OutLimitMin"));
667 info.pidInfo.slewNeg =
James Feist1f802f52019-02-08 13:51:43 -0800668 std::visit(VariantToDoubleVisitor(), base.at("SlewNeg"));
Patrick Venture7442c372019-02-11 10:21:05 -0800669 info.pidInfo.slewPos =
James Feist1f802f52019-02-08 13:51:43 -0800670 std::visit(VariantToDoubleVisitor(), base.at("SlewPos"));
James Feist572c43d2019-01-31 15:52:22 -0800671 double negativeHysteresis = 0;
672 double positiveHysteresis = 0;
673
674 auto findNeg = base.find("NegativeHysteresis");
675 auto findPos = base.find("PositiveHysteresis");
676 if (findNeg != base.end())
677 {
James Feist1f802f52019-02-08 13:51:43 -0800678 negativeHysteresis =
679 std::visit(VariantToDoubleVisitor(), findNeg->second);
James Feist572c43d2019-01-31 15:52:22 -0800680 }
681
682 if (findPos != base.end())
683 {
James Feist1f802f52019-02-08 13:51:43 -0800684 positiveHysteresis =
685 std::visit(VariantToDoubleVisitor(), findPos->second);
James Feist572c43d2019-01-31 15:52:22 -0800686 }
687 info.pidInfo.negativeHysteresis = negativeHysteresis;
688 info.pidInfo.positiveHysteresis = positiveHysteresis;
James Feist22c257a2018-08-31 14:07:12 -0700689 }
690 }
691 auto findStepwise =
692 configuration.second.find(stepwiseConfigurationInterface);
693 if (findStepwise != configuration.second.end())
694 {
695 const auto& base = findStepwise->second;
696 const std::vector<std::string>& zones =
James Feist1f802f52019-02-08 13:51:43 -0800697 std::get<std::vector<std::string>>(base.at("Zones"));
James Feist22c257a2018-08-31 14:07:12 -0700698 for (const std::string& zone : zones)
699 {
James Feistffd418b2018-11-15 14:46:36 -0800700 size_t index = getZoneIndex(zone, foundZones);
James Feistf81f2882019-02-26 11:26:36 -0800701 conf::PIDConf& conf = zoneConfig[index];
James Feist50fdfe32018-09-24 15:51:09 -0700702
703 std::vector<std::string> inputs;
704 std::vector<std::string> sensorNames =
James Feist1f802f52019-02-08 13:51:43 -0800705 std::get<std::vector<std::string>>(base.at("Inputs"));
James Feist50fdfe32018-09-24 15:51:09 -0700706
James Feist1738e2a2019-02-04 15:57:03 -0800707 bool sensorFound = false;
James Feist50fdfe32018-09-24 15:51:09 -0700708 for (const std::string& sensorName : sensorNames)
709 {
710 std::string name = sensorName;
711 // replace spaces with underscores to be legal on dbus
712 std::replace(name.begin(), name.end(), ' ', '_');
James Feist1738e2a2019-02-04 15:57:03 -0800713 std::vector<std::pair<std::string, std::string>>
714 sensorPathIfacePairs;
James Feist50fdfe32018-09-24 15:51:09 -0700715
James Feist1738e2a2019-02-04 15:57:03 -0800716 if (!findSensors(sensors, name, sensorPathIfacePairs))
James Feist50fdfe32018-09-24 15:51:09 -0700717 {
James Feist50fdfe32018-09-24 15:51:09 -0700718 break;
719 }
720
James Feist1738e2a2019-02-04 15:57:03 -0800721 for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
722 {
723 size_t idx =
724 sensorPathIfacePair.first.find_last_of("/") + 1;
725 std::string shortName =
726 sensorPathIfacePair.first.substr(idx);
James Feist50fdfe32018-09-24 15:51:09 -0700727
James Feist1738e2a2019-02-04 15:57:03 -0800728 inputs.push_back(shortName);
729 auto& config = sensorConfig[shortName];
Patrick Venture69c51062019-02-11 09:46:03 -0800730 config.readPath = sensorPathIfacePair.first;
James Feist1738e2a2019-02-04 15:57:03 -0800731 config.type = "temp";
732 // todo: maybe un-hardcode this if we run into slower
733 // timeouts with sensors
734
James Feist2642cb52019-02-25 13:00:16 -0800735 config.timeout = 0;
James Feist1738e2a2019-02-04 15:57:03 -0800736 sensorFound = true;
737 }
James Feist50fdfe32018-09-24 15:51:09 -0700738 }
739 if (!sensorFound)
740 {
741 continue;
742 }
James Feistf81f2882019-02-26 11:26:36 -0800743 struct conf::ControllerInfo& info =
James Feist1f802f52019-02-08 13:51:43 -0800744 conf[std::get<std::string>(base.at("Name"))];
James Feist50fdfe32018-09-24 15:51:09 -0700745 info.inputs = std::move(inputs);
746
James Feist22c257a2018-08-31 14:07:12 -0700747 info.type = "stepwise";
748 info.stepwiseInfo.ts = 1.0; // currently unused
James Feist3dfaafd2018-09-20 15:46:58 -0700749 info.stepwiseInfo.positiveHysteresis = 0.0;
750 info.stepwiseInfo.negativeHysteresis = 0.0;
James Feist608304d2019-02-25 10:01:42 -0800751 std::string subtype = std::get<std::string>(base.at("Class"));
752
753 info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
James Feist3dfaafd2018-09-20 15:46:58 -0700754 auto findPosHyst = base.find("PositiveHysteresis");
755 auto findNegHyst = base.find("NegativeHysteresis");
756 if (findPosHyst != base.end())
757 {
James Feist1f802f52019-02-08 13:51:43 -0800758 info.stepwiseInfo.positiveHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -0800759 VariantToDoubleVisitor(), findPosHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -0700760 }
761 if (findNegHyst != base.end())
762 {
James Feist5782ab82019-04-02 08:38:48 -0700763 info.stepwiseInfo.negativeHysteresis = std::visit(
James Feist208abce2018-12-06 09:59:10 -0800764 VariantToDoubleVisitor(), findNegHyst->second);
James Feist3dfaafd2018-09-20 15:46:58 -0700765 }
James Feist22c257a2018-08-31 14:07:12 -0700766 std::vector<double> readings =
James Feist1f802f52019-02-08 13:51:43 -0800767 std::get<std::vector<double>>(base.at("Reading"));
James Feist22c257a2018-08-31 14:07:12 -0700768 if (readings.size() > ec::maxStepwisePoints)
769 {
770 throw std::invalid_argument("Too many stepwise points.");
771 }
772 if (readings.empty())
773 {
774 throw std::invalid_argument(
775 "Must have one stepwise point.");
776 }
777 std::copy(readings.begin(), readings.end(),
778 info.stepwiseInfo.reading);
779 if (readings.size() < ec::maxStepwisePoints)
780 {
781 info.stepwiseInfo.reading[readings.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800782 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -0700783 }
784 std::vector<double> outputs =
James Feist1f802f52019-02-08 13:51:43 -0800785 std::get<std::vector<double>>(base.at("Output"));
James Feist22c257a2018-08-31 14:07:12 -0700786 if (readings.size() != outputs.size())
787 {
788 throw std::invalid_argument(
789 "Outputs size must match readings");
790 }
791 std::copy(outputs.begin(), outputs.end(),
792 info.stepwiseInfo.output);
793 if (outputs.size() < ec::maxStepwisePoints)
794 {
795 info.stepwiseInfo.output[outputs.size()] =
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800796 std::numeric_limits<double>::quiet_NaN();
James Feist22c257a2018-08-31 14:07:12 -0700797 }
James Feist7136a5a2018-07-19 09:52:05 -0700798 }
799 }
800 }
James Feistf0096a02019-02-21 11:25:22 -0800801 if constexpr (DEBUG)
James Feist7136a5a2018-07-19 09:52:05 -0700802 {
803 debugPrint();
804 }
James Feistc959c422018-11-01 12:33:40 -0700805 if (zoneConfig.empty() || zoneDetailsConfig.empty())
James Feist50fdfe32018-09-24 15:51:09 -0700806 {
James Feist1fe08952019-05-07 09:17:16 -0700807 std::cerr
808 << "No fan zones, application pausing until new configuration\n";
809 return false;
James Feist50fdfe32018-09-24 15:51:09 -0700810 }
James Feist1fe08952019-05-07 09:17:16 -0700811 return true;
James Feist7136a5a2018-07-19 09:52:05 -0700812}
813} // namespace dbus_configuration