17136a5aeSJames Feist /*
27136a5aeSJames Feist // Copyright (c) 2018 Intel Corporation
37136a5aeSJames Feist //
47136a5aeSJames Feist // Licensed under the Apache License, Version 2.0 (the "License");
57136a5aeSJames Feist // you may not use this file except in compliance with the License.
67136a5aeSJames Feist // You may obtain a copy of the License at
77136a5aeSJames Feist //
87136a5aeSJames Feist //      http://www.apache.org/licenses/LICENSE-2.0
97136a5aeSJames Feist //
107136a5aeSJames Feist // Unless required by applicable law or agreed to in writing, software
117136a5aeSJames Feist // distributed under the License is distributed on an "AS IS" BASIS,
127136a5aeSJames Feist // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137136a5aeSJames Feist // See the License for the specific language governing permissions and
147136a5aeSJames Feist // limitations under the License.
157136a5aeSJames Feist */
167136a5aeSJames Feist 
170771659eSPatrick Venture #include "conf.hpp"
180771659eSPatrick Venture #include "dbus/util.hpp"
190771659eSPatrick Venture 
20107a25daSPatrick Venture #include <algorithm>
2164f072a7SJames Feist #include <chrono>
2264f072a7SJames Feist #include <functional>
237136a5aeSJames Feist #include <iostream>
241738e2a2SJames Feist #include <regex>
257136a5aeSJames Feist #include <sdbusplus/bus.hpp>
2664f072a7SJames Feist #include <sdbusplus/bus/match.hpp>
2722c257abSJames Feist #include <sdbusplus/exception.hpp>
287136a5aeSJames Feist #include <set>
2964f072a7SJames Feist #include <thread>
307136a5aeSJames Feist #include <unordered_map>
311f802f5eSJames Feist #include <variant>
327136a5aeSJames Feist 
337136a5aeSJames Feist static constexpr bool DEBUG = false; // enable to print found configuration
347136a5aeSJames Feist 
35f81f2886SJames Feist extern std::map<std::string, struct conf::SensorConfig> sensorConfig;
36f81f2886SJames Feist extern std::map<int64_t, conf::PIDConf> zoneConfig;
37f81f2886SJames Feist extern std::map<int64_t, struct conf::ZoneConfig> zoneDetailsConfig;
387136a5aeSJames Feist 
397136a5aeSJames Feist constexpr const char* pidConfigurationInterface =
407136a5aeSJames Feist     "xyz.openbmc_project.Configuration.Pid";
417136a5aeSJames Feist constexpr const char* objectManagerInterface =
427136a5aeSJames Feist     "org.freedesktop.DBus.ObjectManager";
437136a5aeSJames Feist constexpr const char* pidZoneConfigurationInterface =
447136a5aeSJames Feist     "xyz.openbmc_project.Configuration.Pid.Zone";
4522c257abSJames Feist constexpr const char* stepwiseConfigurationInterface =
4622c257abSJames Feist     "xyz.openbmc_project.Configuration.Stepwise";
47f0096a0cSJames Feist constexpr const char* fanProfileConfigurationIface =
48f0096a0cSJames Feist     "xyz.openbmc_project.Configuration.FanProfile";
49f0096a0cSJames Feist constexpr const char* thermalControlIface =
50f0096a0cSJames Feist     "xyz.openbmc_project.Control.ThermalMode";
517136a5aeSJames Feist constexpr const char* sensorInterface = "xyz.openbmc_project.Sensor.Value";
527136a5aeSJames Feist constexpr const char* pwmInterface = "xyz.openbmc_project.Control.FanPwm";
537136a5aeSJames Feist 
547136a5aeSJames Feist namespace dbus_configuration
557136a5aeSJames Feist {
567136a5aeSJames Feist 
571738e2a2SJames Feist bool findSensors(const std::unordered_map<std::string, std::string>& sensors,
587136a5aeSJames Feist                  const std::string& search,
591738e2a2SJames Feist                  std::vector<std::pair<std::string, std::string>>& matches)
607136a5aeSJames Feist {
611738e2a2SJames Feist     std::smatch match;
621738e2a2SJames Feist     std::regex reg(search);
631738e2a2SJames Feist     for (const auto& sensor : sensors)
647136a5aeSJames Feist     {
651738e2a2SJames Feist         if (std::regex_search(sensor.first, match, reg))
661738e2a2SJames Feist         {
671738e2a2SJames Feist             matches.push_back(sensor);
681738e2a2SJames Feist         }
697136a5aeSJames Feist     }
70107a25daSPatrick Venture 
711738e2a2SJames Feist     return matches.size() > 0;
727136a5aeSJames Feist }
737136a5aeSJames Feist 
747136a5aeSJames Feist // this function prints the configuration into a form similar to the cpp
757136a5aeSJames Feist // generated code to help in verification, should be turned off during normal
767136a5aeSJames Feist // use
777136a5aeSJames Feist void debugPrint(void)
787136a5aeSJames Feist {
797136a5aeSJames Feist     // print sensor config
807136a5aeSJames Feist     std::cout << "sensor config:\n";
817136a5aeSJames Feist     std::cout << "{\n";
82c54fbd88SPatrick Venture     for (const auto& pair : sensorConfig)
837136a5aeSJames Feist     {
847136a5aeSJames Feist 
857136a5aeSJames Feist         std::cout << "\t{" << pair.first << ",\n\t\t{";
867136a5aeSJames Feist         std::cout << pair.second.type << ", ";
8769c51061SPatrick Venture         std::cout << pair.second.readPath << ", ";
8869c51061SPatrick Venture         std::cout << pair.second.writePath << ", ";
897136a5aeSJames Feist         std::cout << pair.second.min << ", ";
907136a5aeSJames Feist         std::cout << pair.second.max << ", ";
917136a5aeSJames Feist         std::cout << pair.second.timeout << "},\n\t},\n";
927136a5aeSJames Feist     }
937136a5aeSJames Feist     std::cout << "}\n\n";
947136a5aeSJames Feist     std::cout << "ZoneDetailsConfig\n";
957136a5aeSJames Feist     std::cout << "{\n";
96c54fbd88SPatrick Venture     for (const auto& zone : zoneDetailsConfig)
977136a5aeSJames Feist     {
987136a5aeSJames Feist         std::cout << "\t{" << zone.first << ",\n";
993484bedaSJames Feist         std::cout << "\t\t{" << zone.second.minThermalOutput << ", ";
1008e2fdb34SPatrick Venture         std::cout << zone.second.failsafePercent << "}\n\t},\n";
1017136a5aeSJames Feist     }
1027136a5aeSJames Feist     std::cout << "}\n\n";
1037136a5aeSJames Feist     std::cout << "ZoneConfig\n";
1047136a5aeSJames Feist     std::cout << "{\n";
105c54fbd88SPatrick Venture     for (const auto& zone : zoneConfig)
1067136a5aeSJames Feist     {
1077136a5aeSJames Feist         std::cout << "\t{" << zone.first << "\n";
1084a2dc4d8SPatrick Venture         for (const auto& pidconf : zone.second)
1097136a5aeSJames Feist         {
1107136a5aeSJames Feist             std::cout << "\t\t{" << pidconf.first << ",\n";
1117136a5aeSJames Feist             std::cout << "\t\t\t{" << pidconf.second.type << ",\n";
1127136a5aeSJames Feist             std::cout << "\t\t\t{";
1134a2dc4d8SPatrick Venture             for (const auto& input : pidconf.second.inputs)
1147136a5aeSJames Feist             {
1157136a5aeSJames Feist                 std::cout << "\n\t\t\t" << input << ",\n";
1167136a5aeSJames Feist             }
1177136a5aeSJames Feist             std::cout << "\t\t\t}\n";
1187136a5aeSJames Feist             std::cout << "\t\t\t" << pidconf.second.setpoint << ",\n";
11922c257abSJames Feist             std::cout << "\t\t\t{" << pidconf.second.pidInfo.ts << ",\n";
1207442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.proportionalCoeff
1217442c37aSPatrick Venture                       << ",\n";
1227442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.integralCoeff
1237442c37aSPatrick Venture                       << ",\n";
1247442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdOffset
1257442c37aSPatrick Venture                       << ",\n";
1267442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdGain
1277442c37aSPatrick Venture                       << ",\n";
1287442c37aSPatrick Venture             std::cout << "\t\t\t{" << pidconf.second.pidInfo.integralLimit.min
1297442c37aSPatrick Venture                       << "," << pidconf.second.pidInfo.integralLimit.max
1307442c37aSPatrick Venture                       << "},\n";
1317442c37aSPatrick Venture             std::cout << "\t\t\t{" << pidconf.second.pidInfo.outLim.min << ","
1327442c37aSPatrick Venture                       << pidconf.second.pidInfo.outLim.max << "},\n";
1337442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.slewNeg << ",\n";
1347442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.slewPos << ",\n";
1357136a5aeSJames Feist             std::cout << "\t\t\t}\n\t\t}\n";
1367136a5aeSJames Feist         }
1377136a5aeSJames Feist         std::cout << "\t},\n";
1387136a5aeSJames Feist     }
1397136a5aeSJames Feist     std::cout << "}\n\n";
1407136a5aeSJames Feist }
1417136a5aeSJames Feist 
14250fdfe39SJames Feist int eventHandler(sd_bus_message*, void*, sd_bus_error*)
14350fdfe39SJames Feist {
14450fdfe39SJames Feist     // do a brief sleep as we tend to get a bunch of these events at
14550fdfe39SJames Feist     // once
14650fdfe39SJames Feist     std::this_thread::sleep_for(std::chrono::seconds(5));
14750fdfe39SJames Feist     std::cout << "New configuration detected, restarting\n.";
14850fdfe39SJames Feist     std::exit(EXIT_SUCCESS); // service file should make us restart
14950fdfe39SJames Feist     return 1;
15050fdfe39SJames Feist }
15150fdfe39SJames Feist 
152ffd418bbSJames Feist size_t getZoneIndex(const std::string& name, std::vector<std::string>& zones)
153ffd418bbSJames Feist {
154ffd418bbSJames Feist     auto it = std::find(zones.begin(), zones.end(), name);
155ffd418bbSJames Feist     if (it == zones.end())
156ffd418bbSJames Feist     {
157ffd418bbSJames Feist         zones.emplace_back(name);
158ffd418bbSJames Feist         it = zones.end() - 1;
159ffd418bbSJames Feist     }
160ffd418bbSJames Feist 
161ffd418bbSJames Feist     return it - zones.begin();
162ffd418bbSJames Feist }
163ffd418bbSJames Feist 
164f0096a0cSJames Feist std::vector<std::string> getSelectedProfiles(sdbusplus::bus::bus& bus)
165f0096a0cSJames Feist {
166f0096a0cSJames Feist     std::vector<std::string> ret;
167f0096a0cSJames Feist     auto mapper =
168f0096a0cSJames Feist         bus.new_method_call("xyz.openbmc_project.ObjectMapper",
169f0096a0cSJames Feist                             "/xyz/openbmc_project/object_mapper",
170f0096a0cSJames Feist                             "xyz.openbmc_project.ObjectMapper", "GetSubTree");
171f0096a0cSJames Feist     mapper.append("/", 0, std::array<const char*, 1>{thermalControlIface});
172f0096a0cSJames Feist     std::unordered_map<
173f0096a0cSJames Feist         std::string, std::unordered_map<std::string, std::vector<std::string>>>
174f0096a0cSJames Feist         respData;
175f0096a0cSJames Feist 
176f0096a0cSJames Feist     try
177f0096a0cSJames Feist     {
178f0096a0cSJames Feist         auto resp = bus.call(mapper);
179f0096a0cSJames Feist         resp.read(respData);
180f0096a0cSJames Feist     }
181f0096a0cSJames Feist     catch (sdbusplus::exception_t&)
182f0096a0cSJames Feist     {
183f0096a0cSJames Feist         // can't do anything without mapper call data
184f0096a0cSJames Feist         throw std::runtime_error("ObjectMapper Call Failure");
185f0096a0cSJames Feist     }
186f0096a0cSJames Feist     if (respData.empty())
187f0096a0cSJames Feist     {
188f0096a0cSJames Feist         // if the user has profiles but doesn't expose the interface to select
189f0096a0cSJames Feist         // one, just go ahead without using profiles
190f0096a0cSJames Feist         return ret;
191f0096a0cSJames Feist     }
192f0096a0cSJames Feist 
193f0096a0cSJames Feist     // assumption is that we should only have a small handful of selected
194f0096a0cSJames Feist     // profiles at a time (probably only 1), so calling each individually should
195f0096a0cSJames Feist     // not incur a large cost
196f0096a0cSJames Feist     for (const auto& objectPair : respData)
197f0096a0cSJames Feist     {
198f0096a0cSJames Feist         const std::string& path = objectPair.first;
199f0096a0cSJames Feist         for (const auto& ownerPair : objectPair.second)
200f0096a0cSJames Feist         {
201f0096a0cSJames Feist             const std::string& busName = ownerPair.first;
202f0096a0cSJames Feist             auto getProfile =
203f0096a0cSJames Feist                 bus.new_method_call(busName.c_str(), path.c_str(),
204f0096a0cSJames Feist                                     "org.freedesktop.DBus.Properties", "Get");
205f0096a0cSJames Feist             getProfile.append(thermalControlIface, "Current");
206f0096a0cSJames Feist             std::variant<std::string> variantResp;
207f0096a0cSJames Feist             try
208f0096a0cSJames Feist             {
209f0096a0cSJames Feist                 auto resp = bus.call(getProfile);
210f0096a0cSJames Feist                 resp.read(variantResp);
211f0096a0cSJames Feist             }
212f0096a0cSJames Feist             catch (sdbusplus::exception_t&)
213f0096a0cSJames Feist             {
214f0096a0cSJames Feist                 throw std::runtime_error("Failure getting profile");
215f0096a0cSJames Feist             }
216f0096a0cSJames Feist             std::string mode = std::get<std::string>(variantResp);
217f0096a0cSJames Feist             ret.emplace_back(std::move(mode));
218f0096a0cSJames Feist         }
219f0096a0cSJames Feist     }
220f0096a0cSJames Feist     if constexpr (DEBUG)
221f0096a0cSJames Feist     {
222f0096a0cSJames Feist         std::cout << "Profiles selected: ";
223f0096a0cSJames Feist         for (const auto& profile : ret)
224f0096a0cSJames Feist         {
225f0096a0cSJames Feist             std::cout << profile << " ";
226f0096a0cSJames Feist         }
227f0096a0cSJames Feist         std::cout << "\n";
228f0096a0cSJames Feist     }
229f0096a0cSJames Feist     return ret;
230f0096a0cSJames Feist }
231f0096a0cSJames Feist 
2327136a5aeSJames Feist void init(sdbusplus::bus::bus& bus)
2337136a5aeSJames Feist {
23422c257abSJames Feist     using DbusVariantType =
2351f802f5eSJames Feist         std::variant<uint64_t, int64_t, double, std::string,
2361f802f5eSJames Feist                      std::vector<std::string>, std::vector<double>>;
23722c257abSJames Feist 
2387136a5aeSJames Feist     using ManagedObjectType = std::unordered_map<
2397136a5aeSJames Feist         sdbusplus::message::object_path,
2407136a5aeSJames Feist         std::unordered_map<std::string,
24122c257abSJames Feist                            std::unordered_map<std::string, DbusVariantType>>>;
24264f072a7SJames Feist 
24350fdfe39SJames Feist     // restart on configuration properties changed
24450fdfe39SJames Feist     static sdbusplus::bus::match::match configMatch(
24564f072a7SJames Feist         bus,
24664f072a7SJames Feist         "type='signal',member='PropertiesChanged',arg0namespace='" +
24764f072a7SJames Feist             std::string(pidConfigurationInterface) + "'",
24864f072a7SJames Feist         eventHandler);
24964f072a7SJames Feist 
250f0096a0cSJames Feist     // restart on profile change
251f0096a0cSJames Feist     static sdbusplus::bus::match::match profileMatch(
252f0096a0cSJames Feist         bus,
253f0096a0cSJames Feist         "type='signal',member='PropertiesChanged',arg0namespace='" +
254f0096a0cSJames Feist             std::string(thermalControlIface) + "'",
255f0096a0cSJames Feist         eventHandler);
256f0096a0cSJames Feist 
25750fdfe39SJames Feist     // restart on sensors changed
25850fdfe39SJames Feist     static sdbusplus::bus::match::match sensorAdded(
25950fdfe39SJames Feist         bus,
26050fdfe39SJames Feist         "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
26150fdfe39SJames Feist         "sensors/'",
26250fdfe39SJames Feist         eventHandler);
26350fdfe39SJames Feist 
2647136a5aeSJames Feist     auto mapper =
2657136a5aeSJames Feist         bus.new_method_call("xyz.openbmc_project.ObjectMapper",
2667136a5aeSJames Feist                             "/xyz/openbmc_project/object_mapper",
2677136a5aeSJames Feist                             "xyz.openbmc_project.ObjectMapper", "GetSubTree");
26826e8c6a9SJames Feist     mapper.append("/", 0,
269f0096a0cSJames Feist                   std::array<const char*, 7>{
270f0096a0cSJames Feist                       objectManagerInterface, pidConfigurationInterface,
2717136a5aeSJames Feist                       pidZoneConfigurationInterface,
272f0096a0cSJames Feist                       stepwiseConfigurationInterface, sensorInterface,
273f0096a0cSJames Feist                       pwmInterface, fanProfileConfigurationIface});
27422c257abSJames Feist     std::unordered_map<
27522c257abSJames Feist         std::string, std::unordered_map<std::string, std::vector<std::string>>>
27622c257abSJames Feist         respData;
27722c257abSJames Feist     try
27822c257abSJames Feist     {
2797136a5aeSJames Feist         auto resp = bus.call(mapper);
2807136a5aeSJames Feist         resp.read(respData);
28122c257abSJames Feist     }
28222c257abSJames Feist     catch (sdbusplus::exception_t&)
28322c257abSJames Feist     {
28422c257abSJames Feist         // can't do anything without mapper call data
28522c257abSJames Feist         throw std::runtime_error("ObjectMapper Call Failure");
28622c257abSJames Feist     }
28722c257abSJames Feist 
2887136a5aeSJames Feist     if (respData.empty())
2897136a5aeSJames Feist     {
29022c257abSJames Feist         // can't do anything without mapper call data
2917136a5aeSJames Feist         throw std::runtime_error("No configuration data available from Mapper");
2927136a5aeSJames Feist     }
2937136a5aeSJames Feist     // create a map of pair of <has pid configuration, ObjectManager path>
2947136a5aeSJames Feist     std::unordered_map<std::string, std::pair<bool, std::string>> owners;
2957136a5aeSJames Feist     // and a map of <path, interface> for sensors
2967136a5aeSJames Feist     std::unordered_map<std::string, std::string> sensors;
2977136a5aeSJames Feist     for (const auto& objectPair : respData)
2987136a5aeSJames Feist     {
2997136a5aeSJames Feist         for (const auto& ownerPair : objectPair.second)
3007136a5aeSJames Feist         {
3017136a5aeSJames Feist             auto& owner = owners[ownerPair.first];
3027136a5aeSJames Feist             for (const std::string& interface : ownerPair.second)
3037136a5aeSJames Feist             {
3047136a5aeSJames Feist 
3057136a5aeSJames Feist                 if (interface == objectManagerInterface)
3067136a5aeSJames Feist                 {
3077136a5aeSJames Feist                     owner.second = objectPair.first;
3087136a5aeSJames Feist                 }
3097136a5aeSJames Feist                 if (interface == pidConfigurationInterface ||
31022c257abSJames Feist                     interface == pidZoneConfigurationInterface ||
31122c257abSJames Feist                     interface == stepwiseConfigurationInterface)
3127136a5aeSJames Feist                 {
3137136a5aeSJames Feist                     owner.first = true;
3147136a5aeSJames Feist                 }
3157136a5aeSJames Feist                 if (interface == sensorInterface || interface == pwmInterface)
3167136a5aeSJames Feist                 {
3177136a5aeSJames Feist                     // we're not interested in pwm sensors, just pwm control
3187136a5aeSJames Feist                     if (interface == sensorInterface &&
3197136a5aeSJames Feist                         objectPair.first.find("pwm") != std::string::npos)
3207136a5aeSJames Feist                     {
3217136a5aeSJames Feist                         continue;
3227136a5aeSJames Feist                     }
3237136a5aeSJames Feist                     sensors[objectPair.first] = interface;
3247136a5aeSJames Feist                 }
3257136a5aeSJames Feist             }
3267136a5aeSJames Feist         }
3277136a5aeSJames Feist     }
3287136a5aeSJames Feist     ManagedObjectType configurations;
329f0096a0cSJames Feist     ManagedObjectType profiles;
3307136a5aeSJames Feist     for (const auto& owner : owners)
3317136a5aeSJames Feist     {
3327136a5aeSJames Feist         // skip if no pid configuration (means probably a sensor)
3337136a5aeSJames Feist         if (!owner.second.first)
3347136a5aeSJames Feist         {
3357136a5aeSJames Feist             continue;
3367136a5aeSJames Feist         }
3377136a5aeSJames Feist         auto endpoint = bus.new_method_call(
3387136a5aeSJames Feist             owner.first.c_str(), owner.second.second.c_str(),
3397136a5aeSJames Feist             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
34022c257abSJames Feist         ManagedObjectType configuration;
34122c257abSJames Feist         try
34222c257abSJames Feist         {
3437136a5aeSJames Feist             auto responce = bus.call(endpoint);
3447136a5aeSJames Feist             responce.read(configuration);
34522c257abSJames Feist         }
34622c257abSJames Feist         catch (sdbusplus::exception_t&)
34722c257abSJames Feist         {
34822c257abSJames Feist             // this shouldn't happen, probably means daemon crashed
34922c257abSJames Feist             throw std::runtime_error("Error getting managed objects from " +
35022c257abSJames Feist                                      owner.first);
35122c257abSJames Feist         }
35222c257abSJames Feist 
3537136a5aeSJames Feist         for (auto& pathPair : configuration)
3547136a5aeSJames Feist         {
3557136a5aeSJames Feist             if (pathPair.second.find(pidConfigurationInterface) !=
3567136a5aeSJames Feist                     pathPair.second.end() ||
3577136a5aeSJames Feist                 pathPair.second.find(pidZoneConfigurationInterface) !=
35822c257abSJames Feist                     pathPair.second.end() ||
35922c257abSJames Feist                 pathPair.second.find(stepwiseConfigurationInterface) !=
3607136a5aeSJames Feist                     pathPair.second.end())
3617136a5aeSJames Feist             {
3627136a5aeSJames Feist                 configurations.emplace(pathPair);
3637136a5aeSJames Feist             }
364f0096a0cSJames Feist             if (pathPair.second.find(fanProfileConfigurationIface) !=
365f0096a0cSJames Feist                 pathPair.second.end())
366f0096a0cSJames Feist             {
367f0096a0cSJames Feist                 profiles.emplace(pathPair);
368f0096a0cSJames Feist             }
369f0096a0cSJames Feist         }
370f0096a0cSJames Feist     }
371f0096a0cSJames Feist 
372f0096a0cSJames Feist     // remove controllers from config that aren't in the current profile(s)
373f0096a0cSJames Feist     if (profiles.size())
374f0096a0cSJames Feist     {
375f0096a0cSJames Feist         std::vector<std::string> selectedProfiles = getSelectedProfiles(bus);
376f0096a0cSJames Feist         if (selectedProfiles.size())
377f0096a0cSJames Feist         {
378f0096a0cSJames Feist             // make the names match the dbus name
379f0096a0cSJames Feist             for (auto& profile : selectedProfiles)
380f0096a0cSJames Feist             {
381f0096a0cSJames Feist                 std::replace(profile.begin(), profile.end(), ' ', '_');
382f0096a0cSJames Feist             }
383f0096a0cSJames Feist 
384f0096a0cSJames Feist             // remove profiles that aren't supported
385f0096a0cSJames Feist             for (auto it = profiles.begin(); it != profiles.end();)
386f0096a0cSJames Feist             {
387f0096a0cSJames Feist                 auto& path = it->first.str;
388f0096a0cSJames Feist                 auto inConfig = std::find_if(
389f0096a0cSJames Feist                     selectedProfiles.begin(), selectedProfiles.end(),
390f0096a0cSJames Feist                     [&path](const std::string& key) {
391f0096a0cSJames Feist                         return (path.find(key) != std::string::npos);
392f0096a0cSJames Feist                     });
393f0096a0cSJames Feist                 if (inConfig == selectedProfiles.end())
394f0096a0cSJames Feist                 {
395f0096a0cSJames Feist                     it = profiles.erase(it);
396f0096a0cSJames Feist                 }
397f0096a0cSJames Feist                 else
398f0096a0cSJames Feist                 {
399f0096a0cSJames Feist                     it++;
400f0096a0cSJames Feist                 }
401f0096a0cSJames Feist             }
402f0096a0cSJames Feist             std::vector<std::string> allowedControllers;
403f0096a0cSJames Feist 
404f0096a0cSJames Feist             // create a vector of profile match strings
405f0096a0cSJames Feist             for (const auto& profile : profiles)
406f0096a0cSJames Feist             {
407f0096a0cSJames Feist                 const auto& interface =
408f0096a0cSJames Feist                     profile.second.at(fanProfileConfigurationIface);
409f0096a0cSJames Feist                 auto findController = interface.find("Controllers");
410f0096a0cSJames Feist                 if (findController == interface.end())
411f0096a0cSJames Feist                 {
412f0096a0cSJames Feist                     throw std::runtime_error("Profile Missing Controllers");
413f0096a0cSJames Feist                 }
414f0096a0cSJames Feist                 std::vector<std::string> controllers =
415f0096a0cSJames Feist                     std::get<std::vector<std::string>>(findController->second);
416f0096a0cSJames Feist                 allowedControllers.insert(allowedControllers.end(),
417f0096a0cSJames Feist                                           controllers.begin(),
418f0096a0cSJames Feist                                           controllers.end());
419f0096a0cSJames Feist             }
420f0096a0cSJames Feist             std::vector<std::regex> regexes;
421f0096a0cSJames Feist             for (auto& controller : allowedControllers)
422f0096a0cSJames Feist             {
423f0096a0cSJames Feist                 std::replace(controller.begin(), controller.end(), ' ', '_');
424f0096a0cSJames Feist                 try
425f0096a0cSJames Feist                 {
426f0096a0cSJames Feist                     regexes.push_back(std::regex(controller));
427f0096a0cSJames Feist                 }
428f0096a0cSJames Feist                 catch (std::regex_error&)
429f0096a0cSJames Feist                 {
430f0096a0cSJames Feist                     std::cerr << "Invalid regex: " << controller << "\n";
431f0096a0cSJames Feist                     throw;
432f0096a0cSJames Feist                 }
433f0096a0cSJames Feist             }
434f0096a0cSJames Feist 
435f0096a0cSJames Feist             // remove configurations that don't match any of the regexes
436f0096a0cSJames Feist             for (auto it = configurations.begin(); it != configurations.end();)
437f0096a0cSJames Feist             {
438f0096a0cSJames Feist                 const std::string& path = it->first;
439f0096a0cSJames Feist                 size_t lastSlash = path.rfind("/");
440f0096a0cSJames Feist                 if (lastSlash == std::string::npos)
441f0096a0cSJames Feist                 {
442f0096a0cSJames Feist                     // if this happens, the mapper has a bug
443f0096a0cSJames Feist                     throw std::runtime_error("Invalid path in configuration");
444f0096a0cSJames Feist                 }
445f0096a0cSJames Feist                 std::string name = path.substr(lastSlash);
446f0096a0cSJames Feist                 auto allowed = std::find_if(
447f0096a0cSJames Feist                     regexes.begin(), regexes.end(), [&name](auto& reg) {
448f0096a0cSJames Feist                         std::smatch match;
449f0096a0cSJames Feist                         return std::regex_search(name, match, reg);
450f0096a0cSJames Feist                     });
451f0096a0cSJames Feist                 if (allowed == regexes.end())
452f0096a0cSJames Feist                 {
453f0096a0cSJames Feist                     auto findZone =
454f0096a0cSJames Feist                         it->second.find(pidZoneConfigurationInterface);
455f0096a0cSJames Feist 
456f0096a0cSJames Feist                     // if there is a fanzone under the given configuration, keep
457f0096a0cSJames Feist                     // it but remove any of the other controllers
458f0096a0cSJames Feist                     if (findZone != it->second.end())
459f0096a0cSJames Feist                     {
460f0096a0cSJames Feist                         for (auto subIt = it->second.begin();
461f0096a0cSJames Feist                              subIt != it->second.end();)
462f0096a0cSJames Feist                         {
463f0096a0cSJames Feist                             if (subIt == findZone)
464f0096a0cSJames Feist                             {
465f0096a0cSJames Feist                                 subIt++;
466f0096a0cSJames Feist                             }
467f0096a0cSJames Feist                             else
468f0096a0cSJames Feist                             {
469f0096a0cSJames Feist                                 subIt = it->second.erase(subIt);
470f0096a0cSJames Feist                             }
471f0096a0cSJames Feist                         }
472f0096a0cSJames Feist                         it++;
473f0096a0cSJames Feist                     }
474f0096a0cSJames Feist                     else
475f0096a0cSJames Feist                     {
476f0096a0cSJames Feist                         it = configurations.erase(it);
477f0096a0cSJames Feist                     }
478f0096a0cSJames Feist                 }
479f0096a0cSJames Feist                 else
480f0096a0cSJames Feist                 {
481f0096a0cSJames Feist                     it++;
482f0096a0cSJames Feist                 }
483f0096a0cSJames Feist             }
4847136a5aeSJames Feist         }
4857136a5aeSJames Feist     }
4868c3c51eeSJames Feist 
4878c3c51eeSJames Feist     // on dbus having an index field is a bit strange, so randomly
4888c3c51eeSJames Feist     // assign index based on name property
489ffd418bbSJames Feist     std::vector<std::string> foundZones;
4907136a5aeSJames Feist     for (const auto& configuration : configurations)
4917136a5aeSJames Feist     {
4927136a5aeSJames Feist         auto findZone =
4937136a5aeSJames Feist             configuration.second.find(pidZoneConfigurationInterface);
4947136a5aeSJames Feist         if (findZone != configuration.second.end())
4957136a5aeSJames Feist         {
4967136a5aeSJames Feist             const auto& zone = findZone->second;
497ffd418bbSJames Feist 
4981f802f5eSJames Feist             const std::string& name = std::get<std::string>(zone.at("Name"));
499ffd418bbSJames Feist             size_t index = getZoneIndex(name, foundZones);
5008c3c51eeSJames Feist 
501c54fbd88SPatrick Venture             auto& details = zoneDetailsConfig[index];
5023484bedaSJames Feist             details.minThermalOutput = std::visit(VariantToDoubleVisitor(),
5033484bedaSJames Feist                                                   zone.at("MinThermalOutput"));
5048e2fdb34SPatrick Venture             details.failsafePercent = std::visit(VariantToDoubleVisitor(),
5051f802f5eSJames Feist                                                  zone.at("FailSafePercent"));
5067136a5aeSJames Feist         }
5077136a5aeSJames Feist         auto findBase = configuration.second.find(pidConfigurationInterface);
50822c257abSJames Feist         if (findBase != configuration.second.end())
5097136a5aeSJames Feist         {
5108c3c51eeSJames Feist 
51122c257abSJames Feist             const auto& base =
51222c257abSJames Feist                 configuration.second.at(pidConfigurationInterface);
5138c3c51eeSJames Feist             const std::vector<std::string>& zones =
5141f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
5158c3c51eeSJames Feist             for (const std::string& zone : zones)
5168c3c51eeSJames Feist             {
517ffd418bbSJames Feist                 size_t index = getZoneIndex(zone, foundZones);
518f81f2886SJames Feist                 conf::PIDConf& conf = zoneConfig[index];
51950fdfe39SJames Feist 
52050fdfe39SJames Feist                 std::vector<std::string> sensorNames =
5211f802f5eSJames Feist                     std::get<std::vector<std::string>>(base.at("Inputs"));
52250fdfe39SJames Feist                 auto findOutputs =
52350fdfe39SJames Feist                     base.find("Outputs"); // currently only fans have outputs
52450fdfe39SJames Feist                 if (findOutputs != base.end())
52550fdfe39SJames Feist                 {
52650fdfe39SJames Feist                     std::vector<std::string> outputs =
5271f802f5eSJames Feist                         std::get<std::vector<std::string>>(findOutputs->second);
52850fdfe39SJames Feist                     sensorNames.insert(sensorNames.end(), outputs.begin(),
52950fdfe39SJames Feist                                        outputs.end());
53050fdfe39SJames Feist                 }
5311738e2a2SJames Feist 
53250fdfe39SJames Feist                 std::vector<std::string> inputs;
5331738e2a2SJames Feist                 std::vector<std::pair<std::string, std::string>>
5341738e2a2SJames Feist                     sensorInterfaces;
53550fdfe39SJames Feist                 for (const std::string& sensorName : sensorNames)
53650fdfe39SJames Feist                 {
53750fdfe39SJames Feist                     std::string name = sensorName;
53850fdfe39SJames Feist                     // replace spaces with underscores to be legal on dbus
53950fdfe39SJames Feist                     std::replace(name.begin(), name.end(), ' ', '_');
5401738e2a2SJames Feist                     findSensors(sensors, name, sensorInterfaces);
54150fdfe39SJames Feist                 }
5421738e2a2SJames Feist 
5431738e2a2SJames Feist                 // if the sensors aren't available in the current state, don't
5441738e2a2SJames Feist                 // add them to the configuration.
5451738e2a2SJames Feist                 if (sensorInterfaces.empty())
5461738e2a2SJames Feist                 {
5471738e2a2SJames Feist                     continue;
5481738e2a2SJames Feist                 }
5491738e2a2SJames Feist                 for (const auto& sensorPathIfacePair : sensorInterfaces)
5501738e2a2SJames Feist                 {
5511738e2a2SJames Feist 
55250fdfe39SJames Feist                     if (sensorPathIfacePair.second == sensorInterface)
55350fdfe39SJames Feist                     {
5541738e2a2SJames Feist                         size_t idx =
5551738e2a2SJames Feist                             sensorPathIfacePair.first.find_last_of("/") + 1;
5561738e2a2SJames Feist                         std::string shortName =
5571738e2a2SJames Feist                             sensorPathIfacePair.first.substr(idx);
5581738e2a2SJames Feist 
5591738e2a2SJames Feist                         inputs.push_back(shortName);
5601738e2a2SJames Feist                         auto& config = sensorConfig[shortName];
5611f802f5eSJames Feist                         config.type = std::get<std::string>(base.at("Class"));
56269c51061SPatrick Venture                         config.readPath = sensorPathIfacePair.first;
56350fdfe39SJames Feist                         // todo: maybe un-hardcode this if we run into slower
56450fdfe39SJames Feist                         // timeouts with sensors
56550fdfe39SJames Feist                         if (config.type == "temp")
56650fdfe39SJames Feist                         {
5672642cb54SJames Feist                             config.timeout = 0;
56850fdfe39SJames Feist                         }
56975eb769dSJames Feist                         else if (config.type == "fan")
57075eb769dSJames Feist                         {
57175eb769dSJames Feist                             config.max = conf::inheritValueFromDbus;
57275eb769dSJames Feist                             config.min = conf::inheritValueFromDbus;
57375eb769dSJames Feist                         }
57450fdfe39SJames Feist                     }
57550fdfe39SJames Feist                     else if (sensorPathIfacePair.second == pwmInterface)
57650fdfe39SJames Feist                     {
57750fdfe39SJames Feist                         // copy so we can modify it
57850fdfe39SJames Feist                         for (std::string otherSensor : sensorNames)
57950fdfe39SJames Feist                         {
5801738e2a2SJames Feist                             std::replace(otherSensor.begin(), otherSensor.end(),
5811738e2a2SJames Feist                                          ' ', '_');
5821738e2a2SJames Feist                             if (sensorPathIfacePair.first.find(otherSensor) !=
5831738e2a2SJames Feist                                 std::string::npos)
58450fdfe39SJames Feist                             {
58550fdfe39SJames Feist                                 continue;
58650fdfe39SJames Feist                             }
5871738e2a2SJames Feist 
588c54fbd88SPatrick Venture                             auto& config = sensorConfig[otherSensor];
58969c51061SPatrick Venture                             config.writePath = sensorPathIfacePair.first;
59050fdfe39SJames Feist                             // todo: un-hardcode this if there are fans with
59150fdfe39SJames Feist                             // different ranges
59250fdfe39SJames Feist                             config.max = 255;
59350fdfe39SJames Feist                             config.min = 0;
59450fdfe39SJames Feist                         }
59550fdfe39SJames Feist                     }
59650fdfe39SJames Feist                 }
5971738e2a2SJames Feist 
598f81f2886SJames Feist                 struct conf::ControllerInfo& info =
5991f802f5eSJames Feist                     conf[std::get<std::string>(base.at("Name"))];
60050fdfe39SJames Feist                 info.inputs = std::move(inputs);
60150fdfe39SJames Feist 
6021f802f5eSJames Feist                 info.type = std::get<std::string>(base.at("Class"));
6038c3c51eeSJames Feist                 // todo: auto generation yaml -> c script seems to discard this
6048c3c51eeSJames Feist                 // value for fans, verify this is okay
6057136a5aeSJames Feist                 if (info.type == "fan")
6067136a5aeSJames Feist                 {
6077136a5aeSJames Feist                     info.setpoint = 0;
6087136a5aeSJames Feist                 }
6097136a5aeSJames Feist                 else
6107136a5aeSJames Feist                 {
6111f802f5eSJames Feist                     info.setpoint = std::visit(VariantToDoubleVisitor(),
612208abce8SJames Feist                                                base.at("SetPoint"));
6137136a5aeSJames Feist                 }
61422c257abSJames Feist                 info.pidInfo.ts = 1.0; // currently unused
6157442c37aSPatrick Venture                 info.pidInfo.proportionalCoeff = std::visit(
6167442c37aSPatrick Venture                     VariantToDoubleVisitor(), base.at("PCoefficient"));
6177442c37aSPatrick Venture                 info.pidInfo.integralCoeff = std::visit(
6187442c37aSPatrick Venture                     VariantToDoubleVisitor(), base.at("ICoefficient"));
6197442c37aSPatrick Venture                 info.pidInfo.feedFwdOffset = std::visit(
6207442c37aSPatrick Venture                     VariantToDoubleVisitor(), base.at("FFOffCoefficient"));
6217442c37aSPatrick Venture                 info.pidInfo.feedFwdGain = std::visit(
6227442c37aSPatrick Venture                     VariantToDoubleVisitor(), base.at("FFGainCoefficient"));
6237442c37aSPatrick Venture                 info.pidInfo.integralLimit.max =
6241f802f5eSJames Feist                     std::visit(VariantToDoubleVisitor(), base.at("ILimitMax"));
6257442c37aSPatrick Venture                 info.pidInfo.integralLimit.min =
6261f802f5eSJames Feist                     std::visit(VariantToDoubleVisitor(), base.at("ILimitMin"));
6277442c37aSPatrick Venture                 info.pidInfo.outLim.max = std::visit(VariantToDoubleVisitor(),
6281f802f5eSJames Feist                                                      base.at("OutLimitMax"));
6297442c37aSPatrick Venture                 info.pidInfo.outLim.min = std::visit(VariantToDoubleVisitor(),
6301f802f5eSJames Feist                                                      base.at("OutLimitMin"));
6317442c37aSPatrick Venture                 info.pidInfo.slewNeg =
6321f802f5eSJames Feist                     std::visit(VariantToDoubleVisitor(), base.at("SlewNeg"));
6337442c37aSPatrick Venture                 info.pidInfo.slewPos =
6341f802f5eSJames Feist                     std::visit(VariantToDoubleVisitor(), base.at("SlewPos"));
635572c43daSJames Feist                 double negativeHysteresis = 0;
636572c43daSJames Feist                 double positiveHysteresis = 0;
637572c43daSJames Feist 
638572c43daSJames Feist                 auto findNeg = base.find("NegativeHysteresis");
639572c43daSJames Feist                 auto findPos = base.find("PositiveHysteresis");
640572c43daSJames Feist                 if (findNeg != base.end())
641572c43daSJames Feist                 {
6421f802f5eSJames Feist                     negativeHysteresis =
6431f802f5eSJames Feist                         std::visit(VariantToDoubleVisitor(), findNeg->second);
644572c43daSJames Feist                 }
645572c43daSJames Feist 
646572c43daSJames Feist                 if (findPos != base.end())
647572c43daSJames Feist                 {
6481f802f5eSJames Feist                     positiveHysteresis =
6491f802f5eSJames Feist                         std::visit(VariantToDoubleVisitor(), findPos->second);
650572c43daSJames Feist                 }
651572c43daSJames Feist                 info.pidInfo.negativeHysteresis = negativeHysteresis;
652572c43daSJames Feist                 info.pidInfo.positiveHysteresis = positiveHysteresis;
6537136a5aeSJames Feist             }
6548c3c51eeSJames Feist         }
65522c257abSJames Feist         auto findStepwise =
65622c257abSJames Feist             configuration.second.find(stepwiseConfigurationInterface);
65722c257abSJames Feist         if (findStepwise != configuration.second.end())
65822c257abSJames Feist         {
65922c257abSJames Feist             const auto& base = findStepwise->second;
66022c257abSJames Feist             const std::vector<std::string>& zones =
6611f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
66222c257abSJames Feist             for (const std::string& zone : zones)
66322c257abSJames Feist             {
664ffd418bbSJames Feist                 size_t index = getZoneIndex(zone, foundZones);
665f81f2886SJames Feist                 conf::PIDConf& conf = zoneConfig[index];
66650fdfe39SJames Feist 
66750fdfe39SJames Feist                 std::vector<std::string> inputs;
66850fdfe39SJames Feist                 std::vector<std::string> sensorNames =
6691f802f5eSJames Feist                     std::get<std::vector<std::string>>(base.at("Inputs"));
67050fdfe39SJames Feist 
6711738e2a2SJames Feist                 bool sensorFound = false;
67250fdfe39SJames Feist                 for (const std::string& sensorName : sensorNames)
67350fdfe39SJames Feist                 {
67450fdfe39SJames Feist                     std::string name = sensorName;
67550fdfe39SJames Feist                     // replace spaces with underscores to be legal on dbus
67650fdfe39SJames Feist                     std::replace(name.begin(), name.end(), ' ', '_');
6771738e2a2SJames Feist                     std::vector<std::pair<std::string, std::string>>
6781738e2a2SJames Feist                         sensorPathIfacePairs;
67950fdfe39SJames Feist 
6801738e2a2SJames Feist                     if (!findSensors(sensors, name, sensorPathIfacePairs))
68150fdfe39SJames Feist                     {
68250fdfe39SJames Feist                         break;
68350fdfe39SJames Feist                     }
68450fdfe39SJames Feist 
6851738e2a2SJames Feist                     for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
6861738e2a2SJames Feist                     {
6871738e2a2SJames Feist                         size_t idx =
6881738e2a2SJames Feist                             sensorPathIfacePair.first.find_last_of("/") + 1;
6891738e2a2SJames Feist                         std::string shortName =
6901738e2a2SJames Feist                             sensorPathIfacePair.first.substr(idx);
6911738e2a2SJames Feist 
6921738e2a2SJames Feist                         inputs.push_back(shortName);
6931738e2a2SJames Feist                         auto& config = sensorConfig[shortName];
69469c51061SPatrick Venture                         config.readPath = sensorPathIfacePair.first;
69550fdfe39SJames Feist                         config.type = "temp";
69650fdfe39SJames Feist                         // todo: maybe un-hardcode this if we run into slower
69750fdfe39SJames Feist                         // timeouts with sensors
69850fdfe39SJames Feist 
6992642cb54SJames Feist                         config.timeout = 0;
7001738e2a2SJames Feist                         sensorFound = true;
7011738e2a2SJames Feist                     }
70250fdfe39SJames Feist                 }
70350fdfe39SJames Feist                 if (!sensorFound)
70450fdfe39SJames Feist                 {
70550fdfe39SJames Feist                     continue;
70650fdfe39SJames Feist                 }
707f81f2886SJames Feist                 struct conf::ControllerInfo& info =
7081f802f5eSJames Feist                     conf[std::get<std::string>(base.at("Name"))];
70950fdfe39SJames Feist                 info.inputs = std::move(inputs);
71050fdfe39SJames Feist 
71122c257abSJames Feist                 info.type = "stepwise";
71222c257abSJames Feist                 info.stepwiseInfo.ts = 1.0; // currently unused
7133dfaafdaSJames Feist                 info.stepwiseInfo.positiveHysteresis = 0.0;
7143dfaafdaSJames Feist                 info.stepwiseInfo.negativeHysteresis = 0.0;
715608304daSJames Feist                 std::string subtype = std::get<std::string>(base.at("Class"));
716608304daSJames Feist 
717608304daSJames Feist                 info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
7183dfaafdaSJames Feist                 auto findPosHyst = base.find("PositiveHysteresis");
7193dfaafdaSJames Feist                 auto findNegHyst = base.find("NegativeHysteresis");
7203dfaafdaSJames Feist                 if (findPosHyst != base.end())
7213dfaafdaSJames Feist                 {
7221f802f5eSJames Feist                     info.stepwiseInfo.positiveHysteresis = std::visit(
723208abce8SJames Feist                         VariantToDoubleVisitor(), findPosHyst->second);
7243dfaafdaSJames Feist                 }
7253dfaafdaSJames Feist                 if (findNegHyst != base.end())
7263dfaafdaSJames Feist                 {
727*5782ab81SJames Feist                     info.stepwiseInfo.negativeHysteresis = std::visit(
728208abce8SJames Feist                         VariantToDoubleVisitor(), findNegHyst->second);
7293dfaafdaSJames Feist                 }
73022c257abSJames Feist                 std::vector<double> readings =
7311f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Reading"));
73222c257abSJames Feist                 if (readings.size() > ec::maxStepwisePoints)
73322c257abSJames Feist                 {
73422c257abSJames Feist                     throw std::invalid_argument("Too many stepwise points.");
73522c257abSJames Feist                 }
73622c257abSJames Feist                 if (readings.empty())
73722c257abSJames Feist                 {
73822c257abSJames Feist                     throw std::invalid_argument(
73922c257abSJames Feist                         "Must have one stepwise point.");
74022c257abSJames Feist                 }
74122c257abSJames Feist                 std::copy(readings.begin(), readings.end(),
74222c257abSJames Feist                           info.stepwiseInfo.reading);
74322c257abSJames Feist                 if (readings.size() < ec::maxStepwisePoints)
74422c257abSJames Feist                 {
74522c257abSJames Feist                     info.stepwiseInfo.reading[readings.size()] =
7465f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
74722c257abSJames Feist                 }
74822c257abSJames Feist                 std::vector<double> outputs =
7491f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Output"));
75022c257abSJames Feist                 if (readings.size() != outputs.size())
75122c257abSJames Feist                 {
75222c257abSJames Feist                     throw std::invalid_argument(
75322c257abSJames Feist                         "Outputs size must match readings");
75422c257abSJames Feist                 }
75522c257abSJames Feist                 std::copy(outputs.begin(), outputs.end(),
75622c257abSJames Feist                           info.stepwiseInfo.output);
75722c257abSJames Feist                 if (outputs.size() < ec::maxStepwisePoints)
75822c257abSJames Feist                 {
75922c257abSJames Feist                     info.stepwiseInfo.output[outputs.size()] =
7605f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
76122c257abSJames Feist                 }
76222c257abSJames Feist             }
76322c257abSJames Feist         }
76422c257abSJames Feist     }
765f0096a0cSJames Feist     if constexpr (DEBUG)
7667136a5aeSJames Feist     {
7677136a5aeSJames Feist         debugPrint();
7687136a5aeSJames Feist     }
769c959c429SJames Feist     if (zoneConfig.empty() || zoneDetailsConfig.empty())
77050fdfe39SJames Feist     {
77150fdfe39SJames Feist         std::cerr << "No fan zones, application pausing until reboot\n";
77250fdfe39SJames Feist         while (1)
77350fdfe39SJames Feist         {
77450fdfe39SJames Feist             bus.process_discard();
77565ea92e7SJames Feist             bus.wait();
77650fdfe39SJames Feist         }
77750fdfe39SJames Feist     }
7787136a5aeSJames Feist }
7797136a5aeSJames Feist } // namespace dbus_configuration
780