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 */
167e952d9fSPatrick Venture #include "dbusconfiguration.hpp"
177136a5aeSJames Feist 
180771659eSPatrick Venture #include "conf.hpp"
19ef1f8864SPatrick Venture #include "dbushelper.hpp"
20ef1f8864SPatrick Venture #include "dbusutil.hpp"
210c8223b5SJames Feist #include "util.hpp"
220771659eSPatrick Venture 
231fe08952SJames Feist #include <boost/asio/steady_timer.hpp>
24a83a3eccSPatrick Venture #include <sdbusplus/bus.hpp>
25a83a3eccSPatrick Venture #include <sdbusplus/bus/match.hpp>
26a83a3eccSPatrick Venture #include <sdbusplus/exception.hpp>
27a83a3eccSPatrick Venture 
28a83a3eccSPatrick Venture #include <algorithm>
2964f072a7SJames Feist #include <chrono>
3064f072a7SJames Feist #include <functional>
317136a5aeSJames Feist #include <iostream>
321fe08952SJames Feist #include <list>
337136a5aeSJames Feist #include <set>
347136a5aeSJames Feist #include <unordered_map>
351f802f5eSJames Feist #include <variant>
367136a5aeSJames Feist 
37a076487aSPatrick Venture namespace pid_control
38a076487aSPatrick Venture {
39a076487aSPatrick Venture 
407136a5aeSJames Feist constexpr const char* pidConfigurationInterface =
417136a5aeSJames Feist     "xyz.openbmc_project.Configuration.Pid";
427136a5aeSJames Feist constexpr const char* objectManagerInterface =
437136a5aeSJames Feist     "org.freedesktop.DBus.ObjectManager";
447136a5aeSJames Feist constexpr const char* pidZoneConfigurationInterface =
457136a5aeSJames Feist     "xyz.openbmc_project.Configuration.Pid.Zone";
4622c257abSJames Feist constexpr const char* stepwiseConfigurationInterface =
4722c257abSJames Feist     "xyz.openbmc_project.Configuration.Stepwise";
48f0096a0cSJames Feist constexpr const char* thermalControlIface =
49f0096a0cSJames Feist     "xyz.openbmc_project.Control.ThermalMode";
507136a5aeSJames Feist constexpr const char* sensorInterface = "xyz.openbmc_project.Sensor.Value";
510911bfe7SPatrick Venture constexpr const char* defaultPwmInterface =
520911bfe7SPatrick Venture     "xyz.openbmc_project.Control.FanPwm";
537136a5aeSJames Feist 
54991ebd80SJames Feist using Association = std::tuple<std::string, std::string, std::string>;
55991ebd80SJames Feist using Associations = std::vector<Association>;
56991ebd80SJames Feist 
575ec20270SJames Feist namespace thresholds
585ec20270SJames Feist {
595ec20270SJames Feist constexpr const char* warningInterface =
605ec20270SJames Feist     "xyz.openbmc_project.Sensor.Threshold.Warning";
615ec20270SJames Feist constexpr const char* criticalInterface =
625ec20270SJames Feist     "xyz.openbmc_project.Sensor.Threshold.Critical";
635ec20270SJames Feist const std::array<const char*, 4> types = {"CriticalLow", "CriticalHigh",
645ec20270SJames Feist                                           "WarningLow", "WarningHigh"};
655ec20270SJames Feist 
665ec20270SJames Feist } // namespace thresholds
675ec20270SJames Feist 
687136a5aeSJames Feist namespace dbus_configuration
697136a5aeSJames Feist {
70f3b04fd3SJason Ling using SensorInterfaceType = std::pair<std::string, std::string>;
71f3b04fd3SJason Ling 
getSensorNameFromPath(const std::string & dbusPath)72f3b04fd3SJason Ling inline std::string getSensorNameFromPath(const std::string& dbusPath)
73f3b04fd3SJason Ling {
74f3b04fd3SJason Ling     return dbusPath.substr(dbusPath.find_last_of("/") + 1);
75f3b04fd3SJason Ling }
76f3b04fd3SJason Ling 
sensorNameToDbusName(const std::string & sensorName)77f3b04fd3SJason Ling inline std::string sensorNameToDbusName(const std::string& sensorName)
78f3b04fd3SJason Ling {
79f3b04fd3SJason Ling     std::string retString = sensorName;
80f3b04fd3SJason Ling     std::replace(retString.begin(), retString.end(), ' ', '_');
81f3b04fd3SJason Ling     return retString;
82f3b04fd3SJason Ling }
835ec20270SJames Feist 
getSelectedProfiles(sdbusplus::bus_t & bus)84b228bc30SPatrick Williams std::vector<std::string> getSelectedProfiles(sdbusplus::bus_t& bus)
85f0096a0cSJames Feist {
86f0096a0cSJames Feist     std::vector<std::string> ret;
878c051121SPatrick Williams     auto mapper = bus.new_method_call("xyz.openbmc_project.ObjectMapper",
88f0096a0cSJames Feist                                       "/xyz/openbmc_project/object_mapper",
898c051121SPatrick Williams                                       "xyz.openbmc_project.ObjectMapper",
908c051121SPatrick Williams                                       "GetSubTree");
91f0096a0cSJames Feist     mapper.append("/", 0, std::array<const char*, 1>{thermalControlIface});
92f0096a0cSJames Feist     std::unordered_map<
93f0096a0cSJames Feist         std::string, std::unordered_map<std::string, std::vector<std::string>>>
94f0096a0cSJames Feist         respData;
95f0096a0cSJames Feist 
96f0096a0cSJames Feist     try
97f0096a0cSJames Feist     {
98f0096a0cSJames Feist         auto resp = bus.call(mapper);
99f0096a0cSJames Feist         resp.read(respData);
100f0096a0cSJames Feist     }
1010001ee02SPatrick Williams     catch (const sdbusplus::exception_t&)
102f0096a0cSJames Feist     {
103f0096a0cSJames Feist         // can't do anything without mapper call data
104f0096a0cSJames Feist         throw std::runtime_error("ObjectMapper Call Failure");
105f0096a0cSJames Feist     }
106f0096a0cSJames Feist     if (respData.empty())
107f0096a0cSJames Feist     {
108f0096a0cSJames Feist         // if the user has profiles but doesn't expose the interface to select
109f0096a0cSJames Feist         // one, just go ahead without using profiles
110f0096a0cSJames Feist         return ret;
111f0096a0cSJames Feist     }
112f0096a0cSJames Feist 
113f0096a0cSJames Feist     // assumption is that we should only have a small handful of selected
114f0096a0cSJames Feist     // profiles at a time (probably only 1), so calling each individually should
115f0096a0cSJames Feist     // not incur a large cost
116f0096a0cSJames Feist     for (const auto& objectPair : respData)
117f0096a0cSJames Feist     {
118f0096a0cSJames Feist         const std::string& path = objectPair.first;
119f0096a0cSJames Feist         for (const auto& ownerPair : objectPair.second)
120f0096a0cSJames Feist         {
121f0096a0cSJames Feist             const std::string& busName = ownerPair.first;
122f0096a0cSJames Feist             auto getProfile =
123f0096a0cSJames Feist                 bus.new_method_call(busName.c_str(), path.c_str(),
124f0096a0cSJames Feist                                     "org.freedesktop.DBus.Properties", "Get");
125f0096a0cSJames Feist             getProfile.append(thermalControlIface, "Current");
126f0096a0cSJames Feist             std::variant<std::string> variantResp;
127f0096a0cSJames Feist             try
128f0096a0cSJames Feist             {
129f0096a0cSJames Feist                 auto resp = bus.call(getProfile);
130f0096a0cSJames Feist                 resp.read(variantResp);
131f0096a0cSJames Feist             }
1320001ee02SPatrick Williams             catch (const sdbusplus::exception_t&)
133f0096a0cSJames Feist             {
134f0096a0cSJames Feist                 throw std::runtime_error("Failure getting profile");
135f0096a0cSJames Feist             }
136f0096a0cSJames Feist             std::string mode = std::get<std::string>(variantResp);
137f0096a0cSJames Feist             ret.emplace_back(std::move(mode));
138f0096a0cSJames Feist         }
139f0096a0cSJames Feist     }
14039199b4dSPatrick Venture     if constexpr (pid_control::conf::DEBUG)
141f0096a0cSJames Feist     {
142f0096a0cSJames Feist         std::cout << "Profiles selected: ";
143f0096a0cSJames Feist         for (const auto& profile : ret)
144f0096a0cSJames Feist         {
145f0096a0cSJames Feist             std::cout << profile << " ";
146f0096a0cSJames Feist         }
147f0096a0cSJames Feist         std::cout << "\n";
148f0096a0cSJames Feist     }
149f0096a0cSJames Feist     return ret;
150f0096a0cSJames Feist }
151f0096a0cSJames Feist 
eventHandler(sd_bus_message * m,void * context,sd_bus_error *)152991ebd80SJames Feist int eventHandler(sd_bus_message* m, void* context, sd_bus_error*)
1537136a5aeSJames Feist {
154991ebd80SJames Feist     if (context == nullptr || m == nullptr)
1551fe08952SJames Feist     {
1561fe08952SJames Feist         throw std::runtime_error("Invalid match");
1571fe08952SJames Feist     }
158991ebd80SJames Feist 
159991ebd80SJames Feist     // we skip associations because the mapper populates these, not the sensors
16010e46efaSJosh Lehan     const std::array<const char*, 2> skipList = {
16110e46efaSJosh Lehan         "xyz.openbmc_project.Association",
16210e46efaSJosh Lehan         "xyz.openbmc_project.Association.Definitions"};
163991ebd80SJames Feist 
164b228bc30SPatrick Williams     sdbusplus::message_t message(m);
165991ebd80SJames Feist     if (std::string(message.get_member()) == "InterfacesAdded")
166991ebd80SJames Feist     {
167991ebd80SJames Feist         sdbusplus::message::object_path path;
168991ebd80SJames Feist         std::unordered_map<
169991ebd80SJames Feist             std::string,
170991ebd80SJames Feist             std::unordered_map<std::string, std::variant<Associations, bool>>>
171991ebd80SJames Feist             data;
172991ebd80SJames Feist 
173991ebd80SJames Feist         message.read(path, data);
174991ebd80SJames Feist 
175991ebd80SJames Feist         for (const char* skip : skipList)
176991ebd80SJames Feist         {
177991ebd80SJames Feist             auto find = data.find(skip);
178991ebd80SJames Feist             if (find != data.end())
179991ebd80SJames Feist             {
180991ebd80SJames Feist                 data.erase(find);
181991ebd80SJames Feist                 if (data.empty())
182991ebd80SJames Feist                 {
183991ebd80SJames Feist                     return 1;
184991ebd80SJames Feist                 }
185991ebd80SJames Feist             }
186991ebd80SJames Feist         }
18710e46efaSJosh Lehan 
18810e46efaSJosh Lehan         if constexpr (pid_control::conf::DEBUG)
18910e46efaSJosh Lehan         {
19010e46efaSJosh Lehan             std::cout << "New config detected: " << path.str << std::endl;
19110e46efaSJosh Lehan             for (auto& d : data)
19210e46efaSJosh Lehan             {
19310e46efaSJosh Lehan                 std::cout << "\tdata is " << d.first << std::endl;
19410e46efaSJosh Lehan                 for (auto& second : d.second)
19510e46efaSJosh Lehan                 {
19610e46efaSJosh Lehan                     std::cout << "\t\tdata is " << second.first << std::endl;
19710e46efaSJosh Lehan                 }
19810e46efaSJosh Lehan             }
19910e46efaSJosh Lehan         }
200991ebd80SJames Feist     }
201991ebd80SJames Feist 
2021fe08952SJames Feist     boost::asio::steady_timer* timer =
2031fe08952SJames Feist         static_cast<boost::asio::steady_timer*>(context);
2041fe08952SJames Feist 
2051fe08952SJames Feist     // do a brief sleep as we tend to get a bunch of these events at
2061fe08952SJames Feist     // once
2071fe08952SJames Feist     timer->expires_after(std::chrono::seconds(2));
2081fe08952SJames Feist     timer->async_wait([](const boost::system::error_code ec) {
2091fe08952SJames Feist         if (ec == boost::asio::error::operation_aborted)
2101fe08952SJames Feist         {
2111fe08952SJames Feist             /* another timer started*/
2121fe08952SJames Feist             return;
2131fe08952SJames Feist         }
2141fe08952SJames Feist 
2151fe08952SJames Feist         std::cout << "New configuration detected, reloading\n.";
216298a95cbSYong Li         tryRestartControlLoops();
2171fe08952SJames Feist     });
2181fe08952SJames Feist 
2191fe08952SJames Feist     return 1;
2201fe08952SJames Feist }
2211fe08952SJames Feist 
createMatches(sdbusplus::bus_t & bus,boost::asio::steady_timer & timer)222b228bc30SPatrick Williams void createMatches(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer)
2231fe08952SJames Feist {
2241fe08952SJames Feist     // this is a list because the matches can't be moved
225b228bc30SPatrick Williams     static std::list<sdbusplus::bus::match_t> matches;
2261fe08952SJames Feist 
2273987c8b4SJames Feist     const std::array<std::string, 4> interfaces = {
2283987c8b4SJames Feist         thermalControlIface, pidConfigurationInterface,
2293987c8b4SJames Feist         pidZoneConfigurationInterface, stepwiseConfigurationInterface};
2301fe08952SJames Feist 
2311fe08952SJames Feist     // this list only needs to be created once
2321fe08952SJames Feist     if (!matches.empty())
2331fe08952SJames Feist     {
2341fe08952SJames Feist         return;
2351fe08952SJames Feist     }
2361fe08952SJames Feist 
2371fe08952SJames Feist     // we restart when the configuration changes or there are new sensors
2381fe08952SJames Feist     for (const auto& interface : interfaces)
2391fe08952SJames Feist     {
2401fe08952SJames Feist         matches.emplace_back(
2411fe08952SJames Feist             bus,
2421fe08952SJames Feist             "type='signal',member='PropertiesChanged',arg0namespace='" +
2431fe08952SJames Feist                 interface + "'",
2441fe08952SJames Feist             eventHandler, &timer);
2451fe08952SJames Feist     }
2461fe08952SJames Feist     matches.emplace_back(
2471fe08952SJames Feist         bus,
2481fe08952SJames Feist         "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
2491fe08952SJames Feist         "sensors/'",
2501fe08952SJames Feist         eventHandler, &timer);
251c2a311b0SJinliang Wang     matches.emplace_back(bus,
252c2a311b0SJinliang Wang                          "type='signal',member='InterfacesRemoved',arg0path='/"
253c2a311b0SJinliang Wang                          "xyz/openbmc_project/sensors/'",
254c2a311b0SJinliang Wang                          eventHandler, &timer);
2551fe08952SJames Feist }
2561fe08952SJames Feist 
2576fc301fbSJason Ling /**
2586fc301fbSJason Ling  * retrieve an attribute from the pid configuration map
2596fc301fbSJason Ling  * @param[in] base - the PID configuration map, keys are the attributes and
2606fc301fbSJason Ling  * value is the variant associated with that attribute.
2616fc301fbSJason Ling  * @param attributeName - the name of the attribute
2626fc301fbSJason Ling  * @return a variant holding the value associated with a key
2636fc301fbSJason Ling  * @throw runtime_error : attributeName is not in base
2646fc301fbSJason Ling  */
getPIDAttribute(const std::unordered_map<std::string,DbusVariantType> & base,const std::string & attributeName)2656fc301fbSJason Ling inline DbusVariantType getPIDAttribute(
2666fc301fbSJason Ling     const std::unordered_map<std::string, DbusVariantType>& base,
2676fc301fbSJason Ling     const std::string& attributeName)
2686fc301fbSJason Ling {
2696fc301fbSJason Ling     auto search = base.find(attributeName);
2706fc301fbSJason Ling     if (search == base.end())
2716fc301fbSJason Ling     {
2726fc301fbSJason Ling         throw std::runtime_error("missing attribute " + attributeName);
2736fc301fbSJason Ling     }
2746fc301fbSJason Ling     return search->second;
2756fc301fbSJason Ling }
2766fc301fbSJason Ling 
getCycleTimeSetting(const std::unordered_map<std::string,DbusVariantType> & zone,const int zoneIndex,const std::string & attributeName,uint64_t & value)277239aa7d7SHarvey Wu inline void getCycleTimeSetting(
278239aa7d7SHarvey Wu     const std::unordered_map<std::string, DbusVariantType>& zone,
279239aa7d7SHarvey Wu     const int zoneIndex, const std::string& attributeName, uint64_t& value)
280239aa7d7SHarvey Wu {
281239aa7d7SHarvey Wu     auto findAttributeName = zone.find(attributeName);
282239aa7d7SHarvey Wu     if (findAttributeName != zone.end())
283239aa7d7SHarvey Wu     {
2848c051121SPatrick Williams         double tmpAttributeValue = std::visit(VariantToDoubleVisitor(),
2858c051121SPatrick Williams                                               zone.at(attributeName));
286239aa7d7SHarvey Wu         if (tmpAttributeValue >= 1.0)
287239aa7d7SHarvey Wu         {
288239aa7d7SHarvey Wu             value = static_cast<uint64_t>(tmpAttributeValue);
289239aa7d7SHarvey Wu         }
290239aa7d7SHarvey Wu         else
291239aa7d7SHarvey Wu         {
292239aa7d7SHarvey Wu             std::cerr << "Zone " << zoneIndex << ": " << attributeName
293239aa7d7SHarvey Wu                       << " is invalid. Use default " << value << " ms\n";
294239aa7d7SHarvey Wu         }
295239aa7d7SHarvey Wu     }
296239aa7d7SHarvey Wu     else
297239aa7d7SHarvey Wu     {
298239aa7d7SHarvey Wu         std::cerr << "Zone " << zoneIndex << ": " << attributeName
299239aa7d7SHarvey Wu                   << " cannot find setting. Use default " << value << " ms\n";
300239aa7d7SHarvey Wu     }
301239aa7d7SHarvey Wu }
302239aa7d7SHarvey Wu 
populatePidInfo(sdbusplus::bus_t & bus,const std::unordered_map<std::string,DbusVariantType> & base,conf::ControllerInfo & info,const std::string * thresholdProperty,const std::map<std::string,conf::SensorConfig> & sensorConfig)3035ec20270SJames Feist void populatePidInfo(
304a1ae4fa1SHarvey.Wu     [[maybe_unused]] sdbusplus::bus_t& bus,
3055ec20270SJames Feist     const std::unordered_map<std::string, DbusVariantType>& base,
3061df9e879SPatrick Venture     conf::ControllerInfo& info, const std::string* thresholdProperty,
3077382318fSPatrick Venture     const std::map<std::string, conf::SensorConfig>& sensorConfig)
3085ec20270SJames Feist {
3096fc301fbSJason Ling     info.type = std::get<std::string>(getPIDAttribute(base, "Class"));
3105ec20270SJames Feist     if (info.type == "fan")
3115ec20270SJames Feist     {
3125ec20270SJames Feist         info.setpoint = 0;
3135ec20270SJames Feist     }
3145ec20270SJames Feist     else
3155ec20270SJames Feist     {
3166fc301fbSJason Ling         info.setpoint = std::visit(VariantToDoubleVisitor(),
3176fc301fbSJason Ling                                    getPIDAttribute(base, "SetPoint"));
3185ec20270SJames Feist     }
3195ec20270SJames Feist 
3209fe3a3c7Sykchiu     int failsafepercent = 0;
3219fe3a3c7Sykchiu     auto findFailSafe = base.find("FailSafePercent");
3229fe3a3c7Sykchiu     if (findFailSafe != base.end())
3239fe3a3c7Sykchiu     {
3249fe3a3c7Sykchiu         failsafepercent = std::visit(VariantToDoubleVisitor(),
3259fe3a3c7Sykchiu                                      getPIDAttribute(base, "FailSafePercent"));
3269fe3a3c7Sykchiu     }
3279fe3a3c7Sykchiu     info.failSafePercent = failsafepercent;
3289fe3a3c7Sykchiu 
3295ec20270SJames Feist     if (thresholdProperty != nullptr)
3305ec20270SJames Feist     {
3315ec20270SJames Feist         std::string interface;
3325ec20270SJames Feist         if (*thresholdProperty == "WarningHigh" ||
3335ec20270SJames Feist             *thresholdProperty == "WarningLow")
3345ec20270SJames Feist         {
3355ec20270SJames Feist             interface = thresholds::warningInterface;
3365ec20270SJames Feist         }
3375ec20270SJames Feist         else
3385ec20270SJames Feist         {
3395ec20270SJames Feist             interface = thresholds::criticalInterface;
3405ec20270SJames Feist         }
34131058fd3SJosh Lehan 
34231058fd3SJosh Lehan         // Although this checks only the first vector element for the
34331058fd3SJosh Lehan         // named threshold, it is OK, because the SetPointOffset parser
34431058fd3SJosh Lehan         // splits up the input into individual vectors, each with only a
34531058fd3SJosh Lehan         // single element, if it detects that SetPointOffset is in use.
34631058fd3SJosh Lehan         const std::string& path =
34731058fd3SJosh Lehan             sensorConfig.at(info.inputs.front().name).readPath;
3485ec20270SJames Feist 
3498729eb98SPatrick Venture         DbusHelper helper(sdbusplus::bus::new_system());
3509b93692dSPatrick Venture         std::string service = helper.getService(interface, path);
3515ec20270SJames Feist         double reading = 0;
3525ec20270SJames Feist         try
3535ec20270SJames Feist         {
3549b93692dSPatrick Venture             helper.getProperty(service, path, interface, *thresholdProperty,
3559b93692dSPatrick Venture                                reading);
3565ec20270SJames Feist         }
357b228bc30SPatrick Williams         catch (const sdbusplus::exception_t& ex)
3585ec20270SJames Feist         {
3595ec20270SJames Feist             // unsupported threshold, leaving reading at 0
3605ec20270SJames Feist         }
3615ec20270SJames Feist 
3625ec20270SJames Feist         info.setpoint += reading;
3635ec20270SJames Feist     }
3645ec20270SJames Feist 
3655ec20270SJames Feist     info.pidInfo.ts = 1.0; // currently unused
3666fc301fbSJason Ling     info.pidInfo.proportionalCoeff = std::visit(
3676fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "PCoefficient"));
3686fc301fbSJason Ling     info.pidInfo.integralCoeff = std::visit(
3696fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ICoefficient"));
370c612c051SJosh Lehan     // DCoefficient is below, it is optional, same reason as in buildjson.cpp
3716fc301fbSJason Ling     info.pidInfo.feedFwdOffset = std::visit(
3726fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "FFOffCoefficient"));
3736fc301fbSJason Ling     info.pidInfo.feedFwdGain = std::visit(
3746fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "FFGainCoefficient"));
3756fc301fbSJason Ling     info.pidInfo.integralLimit.max = std::visit(
3766fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMax"));
3776fc301fbSJason Ling     info.pidInfo.integralLimit.min = std::visit(
3786fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMin"));
3796fc301fbSJason Ling     info.pidInfo.outLim.max = std::visit(VariantToDoubleVisitor(),
3806fc301fbSJason Ling                                          getPIDAttribute(base, "OutLimitMax"));
3816fc301fbSJason Ling     info.pidInfo.outLim.min = std::visit(VariantToDoubleVisitor(),
3826fc301fbSJason Ling                                          getPIDAttribute(base, "OutLimitMin"));
3838c051121SPatrick Williams     info.pidInfo.slewNeg = std::visit(VariantToDoubleVisitor(),
3848c051121SPatrick Williams                                       getPIDAttribute(base, "SlewNeg"));
3858c051121SPatrick Williams     info.pidInfo.slewPos = std::visit(VariantToDoubleVisitor(),
3868c051121SPatrick Williams                                       getPIDAttribute(base, "SlewPos"));
387c612c051SJosh Lehan 
3889788963cSDelphine CC Chiu     bool checkHysterWithSetpt = false;
3895ec20270SJames Feist     double negativeHysteresis = 0;
3905ec20270SJames Feist     double positiveHysteresis = 0;
391c612c051SJosh Lehan     double derivativeCoeff = 0;
3925ec20270SJames Feist 
393*5d897e2aSDelphine CC Chiu     auto findCheckHysterFlag = base.find("CheckHysteresisWithSetpoint");
3945ec20270SJames Feist     auto findNeg = base.find("NegativeHysteresis");
3955ec20270SJames Feist     auto findPos = base.find("PositiveHysteresis");
396c612c051SJosh Lehan     auto findDerivative = base.find("DCoefficient");
3975ec20270SJames Feist 
3989788963cSDelphine CC Chiu     if (findCheckHysterFlag != base.end())
3999788963cSDelphine CC Chiu     {
4009788963cSDelphine CC Chiu         checkHysterWithSetpt = std::get<bool>(findCheckHysterFlag->second);
4019788963cSDelphine CC Chiu     }
4025ec20270SJames Feist     if (findNeg != base.end())
4035ec20270SJames Feist     {
4048c051121SPatrick Williams         negativeHysteresis = std::visit(VariantToDoubleVisitor(),
4058c051121SPatrick Williams                                         findNeg->second);
4065ec20270SJames Feist     }
4075ec20270SJames Feist     if (findPos != base.end())
4085ec20270SJames Feist     {
4098c051121SPatrick Williams         positiveHysteresis = std::visit(VariantToDoubleVisitor(),
4108c051121SPatrick Williams                                         findPos->second);
4115ec20270SJames Feist     }
412c612c051SJosh Lehan     if (findDerivative != base.end())
413c612c051SJosh Lehan     {
4148c051121SPatrick Williams         derivativeCoeff = std::visit(VariantToDoubleVisitor(),
4158c051121SPatrick Williams                                      findDerivative->second);
416c612c051SJosh Lehan     }
417c612c051SJosh Lehan 
4189788963cSDelphine CC Chiu     info.pidInfo.checkHysterWithSetpt = checkHysterWithSetpt;
4195ec20270SJames Feist     info.pidInfo.negativeHysteresis = negativeHysteresis;
4205ec20270SJames Feist     info.pidInfo.positiveHysteresis = positiveHysteresis;
421c612c051SJosh Lehan     info.pidInfo.derivativeCoeff = derivativeCoeff;
4225ec20270SJames Feist }
4235ec20270SJames Feist 
init(sdbusplus::bus_t & bus,boost::asio::steady_timer & timer,std::map<std::string,conf::SensorConfig> & sensorConfig,std::map<int64_t,conf::PIDConf> & zoneConfig,std::map<int64_t,conf::ZoneConfig> & zoneDetailsConfig)424b228bc30SPatrick Williams bool init(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer,
4257382318fSPatrick Venture           std::map<std::string, conf::SensorConfig>& sensorConfig,
4267382318fSPatrick Venture           std::map<int64_t, conf::PIDConf>& zoneConfig,
4277382318fSPatrick Venture           std::map<int64_t, conf::ZoneConfig>& zoneDetailsConfig)
4281fe08952SJames Feist {
4291fe08952SJames Feist     sensorConfig.clear();
4301fe08952SJames Feist     zoneConfig.clear();
4311fe08952SJames Feist     zoneDetailsConfig.clear();
4321fe08952SJames Feist 
4331fe08952SJames Feist     createMatches(bus, timer);
4341fe08952SJames Feist 
4358c051121SPatrick Williams     auto mapper = bus.new_method_call("xyz.openbmc_project.ObjectMapper",
4367136a5aeSJames Feist                                       "/xyz/openbmc_project/object_mapper",
4378c051121SPatrick Williams                                       "xyz.openbmc_project.ObjectMapper",
4388c051121SPatrick Williams                                       "GetSubTree");
43926e8c6a9SJames Feist     mapper.append("/", 0,
4400911bfe7SPatrick Venture                   std::array<const char*, 6>{
4410911bfe7SPatrick Venture                       objectManagerInterface, pidConfigurationInterface,
4427136a5aeSJames Feist                       pidZoneConfigurationInterface,
4430911bfe7SPatrick Venture                       stepwiseConfigurationInterface, sensorInterface,
4440911bfe7SPatrick Venture                       defaultPwmInterface});
44522c257abSJames Feist     std::unordered_map<
44622c257abSJames Feist         std::string, std::unordered_map<std::string, std::vector<std::string>>>
44722c257abSJames Feist         respData;
44822c257abSJames Feist     try
44922c257abSJames Feist     {
4507136a5aeSJames Feist         auto resp = bus.call(mapper);
4517136a5aeSJames Feist         resp.read(respData);
45222c257abSJames Feist     }
4530001ee02SPatrick Williams     catch (const sdbusplus::exception_t&)
45422c257abSJames Feist     {
45522c257abSJames Feist         // can't do anything without mapper call data
45622c257abSJames Feist         throw std::runtime_error("ObjectMapper Call Failure");
45722c257abSJames Feist     }
45822c257abSJames Feist 
4597136a5aeSJames Feist     if (respData.empty())
4607136a5aeSJames Feist     {
46122c257abSJames Feist         // can't do anything without mapper call data
4627136a5aeSJames Feist         throw std::runtime_error("No configuration data available from Mapper");
4637136a5aeSJames Feist     }
4647136a5aeSJames Feist     // create a map of pair of <has pid configuration, ObjectManager path>
4657136a5aeSJames Feist     std::unordered_map<std::string, std::pair<bool, std::string>> owners;
4667136a5aeSJames Feist     // and a map of <path, interface> for sensors
4677136a5aeSJames Feist     std::unordered_map<std::string, std::string> sensors;
4687136a5aeSJames Feist     for (const auto& objectPair : respData)
4697136a5aeSJames Feist     {
4707136a5aeSJames Feist         for (const auto& ownerPair : objectPair.second)
4717136a5aeSJames Feist         {
4727136a5aeSJames Feist             auto& owner = owners[ownerPair.first];
4737136a5aeSJames Feist             for (const std::string& interface : ownerPair.second)
4747136a5aeSJames Feist             {
4757136a5aeSJames Feist                 if (interface == objectManagerInterface)
4767136a5aeSJames Feist                 {
4777136a5aeSJames Feist                     owner.second = objectPair.first;
4787136a5aeSJames Feist                 }
4797136a5aeSJames Feist                 if (interface == pidConfigurationInterface ||
48022c257abSJames Feist                     interface == pidZoneConfigurationInterface ||
48122c257abSJames Feist                     interface == stepwiseConfigurationInterface)
4827136a5aeSJames Feist                 {
4837136a5aeSJames Feist                     owner.first = true;
4847136a5aeSJames Feist                 }
4850911bfe7SPatrick Venture                 if (interface == sensorInterface ||
4860911bfe7SPatrick Venture                     interface == defaultPwmInterface)
4877136a5aeSJames Feist                 {
4887136a5aeSJames Feist                     // we're not interested in pwm sensors, just pwm control
4897136a5aeSJames Feist                     if (interface == sensorInterface &&
4907136a5aeSJames Feist                         objectPair.first.find("pwm") != std::string::npos)
4917136a5aeSJames Feist                     {
4927136a5aeSJames Feist                         continue;
4937136a5aeSJames Feist                     }
4947136a5aeSJames Feist                     sensors[objectPair.first] = interface;
4957136a5aeSJames Feist                 }
4967136a5aeSJames Feist             }
4977136a5aeSJames Feist         }
4987136a5aeSJames Feist     }
4997136a5aeSJames Feist     ManagedObjectType configurations;
5007136a5aeSJames Feist     for (const auto& owner : owners)
5017136a5aeSJames Feist     {
5027136a5aeSJames Feist         // skip if no pid configuration (means probably a sensor)
5037136a5aeSJames Feist         if (!owner.second.first)
5047136a5aeSJames Feist         {
5057136a5aeSJames Feist             continue;
5067136a5aeSJames Feist         }
5077136a5aeSJames Feist         auto endpoint = bus.new_method_call(
5087136a5aeSJames Feist             owner.first.c_str(), owner.second.second.c_str(),
5097136a5aeSJames Feist             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
51022c257abSJames Feist         ManagedObjectType configuration;
51122c257abSJames Feist         try
51222c257abSJames Feist         {
5137136a5aeSJames Feist             auto responce = bus.call(endpoint);
5147136a5aeSJames Feist             responce.read(configuration);
51522c257abSJames Feist         }
5160001ee02SPatrick Williams         catch (const sdbusplus::exception_t&)
51722c257abSJames Feist         {
51822c257abSJames Feist             // this shouldn't happen, probably means daemon crashed
51922c257abSJames Feist             throw std::runtime_error("Error getting managed objects from " +
52022c257abSJames Feist                                      owner.first);
52122c257abSJames Feist         }
52222c257abSJames Feist 
5237136a5aeSJames Feist         for (auto& pathPair : configuration)
5247136a5aeSJames Feist         {
5257136a5aeSJames Feist             if (pathPair.second.find(pidConfigurationInterface) !=
5267136a5aeSJames Feist                     pathPair.second.end() ||
5277136a5aeSJames Feist                 pathPair.second.find(pidZoneConfigurationInterface) !=
52822c257abSJames Feist                     pathPair.second.end() ||
52922c257abSJames Feist                 pathPair.second.find(stepwiseConfigurationInterface) !=
5307136a5aeSJames Feist                     pathPair.second.end())
5317136a5aeSJames Feist             {
5327136a5aeSJames Feist                 configurations.emplace(pathPair);
5337136a5aeSJames Feist             }
534f0096a0cSJames Feist         }
535f0096a0cSJames Feist     }
536f0096a0cSJames Feist 
537f0096a0cSJames Feist     // remove controllers from config that aren't in the current profile(s)
538f0096a0cSJames Feist     std::vector<std::string> selectedProfiles = getSelectedProfiles(bus);
539f0096a0cSJames Feist     if (selectedProfiles.size())
540f0096a0cSJames Feist     {
5413987c8b4SJames Feist         for (auto pathIt = configurations.begin();
5423987c8b4SJames Feist              pathIt != configurations.end();)
543f0096a0cSJames Feist         {
5443987c8b4SJames Feist             for (auto confIt = pathIt->second.begin();
5453987c8b4SJames Feist                  confIt != pathIt->second.end();)
5463987c8b4SJames Feist             {
5473987c8b4SJames Feist                 auto profilesFind = confIt->second.find("Profiles");
5483987c8b4SJames Feist                 if (profilesFind == confIt->second.end())
5493987c8b4SJames Feist                 {
5503987c8b4SJames Feist                     confIt++;
5513987c8b4SJames Feist                     continue; // if no profiles selected, apply always
5523987c8b4SJames Feist                 }
5533987c8b4SJames Feist                 auto profiles =
5543987c8b4SJames Feist                     std::get<std::vector<std::string>>(profilesFind->second);
5553987c8b4SJames Feist                 if (profiles.empty())
5563987c8b4SJames Feist                 {
5573987c8b4SJames Feist                     confIt++;
5583987c8b4SJames Feist                     continue;
559f0096a0cSJames Feist                 }
560f0096a0cSJames Feist 
5613987c8b4SJames Feist                 bool found = false;
5623987c8b4SJames Feist                 for (const std::string& profile : profiles)
563f0096a0cSJames Feist                 {
5643987c8b4SJames Feist                     if (std::find(selectedProfiles.begin(),
5653987c8b4SJames Feist                                   selectedProfiles.end(),
5663987c8b4SJames Feist                                   profile) != selectedProfiles.end())
567f0096a0cSJames Feist                     {
5683987c8b4SJames Feist                         found = true;
5693987c8b4SJames Feist                         break;
5703987c8b4SJames Feist                     }
5713987c8b4SJames Feist                 }
5723987c8b4SJames Feist                 if (found)
5733987c8b4SJames Feist                 {
5743987c8b4SJames Feist                     confIt++;
575f0096a0cSJames Feist                 }
576f0096a0cSJames Feist                 else
577f0096a0cSJames Feist                 {
5783987c8b4SJames Feist                     confIt = pathIt->second.erase(confIt);
579f0096a0cSJames Feist                 }
580f0096a0cSJames Feist             }
5813987c8b4SJames Feist             if (pathIt->second.empty())
582f0096a0cSJames Feist             {
5833987c8b4SJames Feist                 pathIt = configurations.erase(pathIt);
584f0096a0cSJames Feist             }
585f0096a0cSJames Feist             else
586f0096a0cSJames Feist             {
5873987c8b4SJames Feist                 pathIt++;
588f0096a0cSJames Feist             }
5897136a5aeSJames Feist         }
5907136a5aeSJames Feist     }
5918c3c51eeSJames Feist 
592998fbe67SJosh Lehan     // On D-Bus, although not necessary,
593998fbe67SJosh Lehan     // having the "zoneID" field can still be useful,
594998fbe67SJosh Lehan     // as it is used for diagnostic messages,
595998fbe67SJosh Lehan     // logging file names, and so on.
596998fbe67SJosh Lehan     // Accept optional "ZoneIndex" parameter to explicitly specify.
597998fbe67SJosh Lehan     // If not present, or not unique, auto-assign index,
598998fbe67SJosh Lehan     // using 0-based numbering, ensuring uniqueness.
599998fbe67SJosh Lehan     std::map<std::string, int64_t> foundZones;
6007136a5aeSJames Feist     for (const auto& configuration : configurations)
6017136a5aeSJames Feist     {
6027136a5aeSJames Feist         auto findZone =
6037136a5aeSJames Feist             configuration.second.find(pidZoneConfigurationInterface);
6047136a5aeSJames Feist         if (findZone != configuration.second.end())
6057136a5aeSJames Feist         {
6067136a5aeSJames Feist             const auto& zone = findZone->second;
607ffd418bbSJames Feist 
6081f802f5eSJames Feist             const std::string& name = std::get<std::string>(zone.at("Name"));
609998fbe67SJosh Lehan 
610998fbe67SJosh Lehan             auto findZoneIndex = zone.find("ZoneIndex");
611998fbe67SJosh Lehan             if (findZoneIndex == zone.end())
612998fbe67SJosh Lehan             {
613998fbe67SJosh Lehan                 continue;
614998fbe67SJosh Lehan             }
615998fbe67SJosh Lehan 
616998fbe67SJosh Lehan             auto ptrZoneIndex = std::get_if<double>(&(findZoneIndex->second));
617998fbe67SJosh Lehan             if (!ptrZoneIndex)
618998fbe67SJosh Lehan             {
619998fbe67SJosh Lehan                 continue;
620998fbe67SJosh Lehan             }
621998fbe67SJosh Lehan 
622998fbe67SJosh Lehan             auto desiredIndex = static_cast<int64_t>(*ptrZoneIndex);
623998fbe67SJosh Lehan             auto grantedIndex = setZoneIndex(name, foundZones, desiredIndex);
624998fbe67SJosh Lehan             std::cout << "Zone " << name << " is at ZoneIndex " << grantedIndex
625998fbe67SJosh Lehan                       << "\n";
626998fbe67SJosh Lehan         }
627998fbe67SJosh Lehan     }
628998fbe67SJosh Lehan 
629998fbe67SJosh Lehan     for (const auto& configuration : configurations)
630998fbe67SJosh Lehan     {
631998fbe67SJosh Lehan         auto findZone =
632998fbe67SJosh Lehan             configuration.second.find(pidZoneConfigurationInterface);
633998fbe67SJosh Lehan         if (findZone != configuration.second.end())
634998fbe67SJosh Lehan         {
635998fbe67SJosh Lehan             const auto& zone = findZone->second;
636998fbe67SJosh Lehan 
637998fbe67SJosh Lehan             const std::string& name = std::get<std::string>(zone.at("Name"));
638998fbe67SJosh Lehan 
639998fbe67SJosh Lehan             auto index = getZoneIndex(name, foundZones);
6408c3c51eeSJames Feist 
641c54fbd88SPatrick Venture             auto& details = zoneDetailsConfig[index];
642998fbe67SJosh Lehan 
6433484bedaSJames Feist             details.minThermalOutput = std::visit(VariantToDoubleVisitor(),
6443484bedaSJames Feist                                                   zone.at("MinThermalOutput"));
6459fe3a3c7Sykchiu 
6469fe3a3c7Sykchiu             int failsafepercent = 0;
6479fe3a3c7Sykchiu             auto findFailSafe = zone.find("FailSafePercent");
6489fe3a3c7Sykchiu             if (findFailSafe != zone.end())
6499fe3a3c7Sykchiu             {
6509fe3a3c7Sykchiu                 failsafepercent = std::visit(VariantToDoubleVisitor(),
6511f802f5eSJames Feist                                              zone.at("FailSafePercent"));
6529fe3a3c7Sykchiu             }
6539fe3a3c7Sykchiu             details.failsafePercent = failsafepercent;
6549f9a06aaSJosh Lehan 
655239aa7d7SHarvey Wu             getCycleTimeSetting(zone, index, "CycleIntervalTimeMS",
656239aa7d7SHarvey Wu                                 details.cycleTime.cycleIntervalTimeMS);
657239aa7d7SHarvey Wu             getCycleTimeSetting(zone, index, "UpdateThermalsTimeMS",
658239aa7d7SHarvey Wu                                 details.cycleTime.updateThermalsTimeMS);
6599788963cSDelphine CC Chiu 
6609788963cSDelphine CC Chiu             bool accumulateSetPoint = false;
6619788963cSDelphine CC Chiu             auto findAccSetPoint = zone.find("AccumulateSetPoint");
6629788963cSDelphine CC Chiu             if (findAccSetPoint != zone.end())
6639788963cSDelphine CC Chiu             {
6649788963cSDelphine CC Chiu                 accumulateSetPoint = std::get<bool>(findAccSetPoint->second);
6659788963cSDelphine CC Chiu             }
6669788963cSDelphine CC Chiu             details.accumulateSetPoint = accumulateSetPoint;
6677136a5aeSJames Feist         }
6687136a5aeSJames Feist         auto findBase = configuration.second.find(pidConfigurationInterface);
669f3b04fd3SJason Ling         // loop through all the PID configurations and fill out a sensor config
67022c257abSJames Feist         if (findBase != configuration.second.end())
6717136a5aeSJames Feist         {
67222c257abSJames Feist             const auto& base =
67322c257abSJames Feist                 configuration.second.at(pidConfigurationInterface);
6747c6d35d5Sykchiu             const std::string pidName =
6757c6d35d5Sykchiu                 sensorNameToDbusName(std::get<std::string>(base.at("Name")));
676f3b04fd3SJason Ling             const std::string pidClass =
677f3b04fd3SJason Ling                 std::get<std::string>(base.at("Class"));
6788c3c51eeSJames Feist             const std::vector<std::string>& zones =
6791f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
6808c3c51eeSJames Feist             for (const std::string& zone : zones)
6818c3c51eeSJames Feist             {
682998fbe67SJosh Lehan                 auto index = getZoneIndex(zone, foundZones);
683998fbe67SJosh Lehan 
684f81f2886SJames Feist                 conf::PIDConf& conf = zoneConfig[index];
685f3b04fd3SJason Ling                 std::vector<std::string> inputSensorNames(
686f3b04fd3SJason Ling                     std::get<std::vector<std::string>>(base.at("Inputs")));
687f3b04fd3SJason Ling                 std::vector<std::string> outputSensorNames;
6883f0f7bc3SJosh Lehan                 std::vector<std::string> missingAcceptableSensorNames;
6893f0f7bc3SJosh Lehan 
6903f0f7bc3SJosh Lehan                 auto findMissingAcceptable = base.find("MissingIsAcceptable");
6913f0f7bc3SJosh Lehan                 if (findMissingAcceptable != base.end())
6923f0f7bc3SJosh Lehan                 {
6933f0f7bc3SJosh Lehan                     missingAcceptableSensorNames =
6943f0f7bc3SJosh Lehan                         std::get<std::vector<std::string>>(
6953f0f7bc3SJosh Lehan                             findMissingAcceptable->second);
6963f0f7bc3SJosh Lehan                 }
69750fdfe39SJames Feist 
698f3b04fd3SJason Ling                 // assumption: all fan pids must have at least one output
699f3b04fd3SJason Ling                 if (pidClass == "fan")
70050fdfe39SJames Feist                 {
701f3b04fd3SJason Ling                     outputSensorNames = std::get<std::vector<std::string>>(
702f3b04fd3SJason Ling                         getPIDAttribute(base, "Outputs"));
70350fdfe39SJames Feist                 }
7041738e2a2SJames Feist 
7058f73ad76SAlex.Song                 bool unavailableAsFailed = true;
7068f73ad76SAlex.Song                 auto findUnavailableAsFailed =
7078f73ad76SAlex.Song                     base.find("InputUnavailableAsFailed");
7088f73ad76SAlex.Song                 if (findUnavailableAsFailed != base.end())
7098f73ad76SAlex.Song                 {
7108f73ad76SAlex.Song                     unavailableAsFailed =
7118f73ad76SAlex.Song                         std::get<bool>(findUnavailableAsFailed->second);
7128f73ad76SAlex.Song                 }
7138f73ad76SAlex.Song 
714f3b04fd3SJason Ling                 std::vector<SensorInterfaceType> inputSensorInterfaces;
715f3b04fd3SJason Ling                 std::vector<SensorInterfaceType> outputSensorInterfaces;
7163f0f7bc3SJosh Lehan                 std::vector<SensorInterfaceType>
7173f0f7bc3SJosh Lehan                     missingAcceptableSensorInterfaces;
7183f0f7bc3SJosh Lehan 
719f3b04fd3SJason Ling                 /* populate an interface list for different sensor direction
720f3b04fd3SJason Ling                  * types (input,output)
721f3b04fd3SJason Ling                  */
722f3b04fd3SJason Ling                 /* take the Inputs from the configuration and generate
723f3b04fd3SJason Ling                  * a list of dbus descriptors (path, interface).
724f3b04fd3SJason Ling                  * Mapping can be many-to-one since an element of Inputs can be
725f3b04fd3SJason Ling                  * a regex
726f3b04fd3SJason Ling                  */
727f3b04fd3SJason Ling                 for (const std::string& sensorName : inputSensorNames)
72850fdfe39SJames Feist                 {
729f3b04fd3SJason Ling                     findSensors(sensors, sensorNameToDbusName(sensorName),
730f3b04fd3SJason Ling                                 inputSensorInterfaces);
731f3b04fd3SJason Ling                 }
732f3b04fd3SJason Ling                 for (const std::string& sensorName : outputSensorNames)
733f3b04fd3SJason Ling                 {
734f3b04fd3SJason Ling                     findSensors(sensors, sensorNameToDbusName(sensorName),
735f3b04fd3SJason Ling                                 outputSensorInterfaces);
73650fdfe39SJames Feist                 }
7373f0f7bc3SJosh Lehan                 for (const std::string& sensorName :
7383f0f7bc3SJosh Lehan                      missingAcceptableSensorNames)
7393f0f7bc3SJosh Lehan                 {
7403f0f7bc3SJosh Lehan                     findSensors(sensors, sensorNameToDbusName(sensorName),
7413f0f7bc3SJosh Lehan                                 missingAcceptableSensorInterfaces);
7423f0f7bc3SJosh Lehan                 }
7431738e2a2SJames Feist 
744f3b04fd3SJason Ling                 inputSensorNames.clear();
745f3b04fd3SJason Ling                 for (const SensorInterfaceType& inputSensorInterface :
746f3b04fd3SJason Ling                      inputSensorInterfaces)
7471738e2a2SJames Feist                 {
748f3b04fd3SJason Ling                     const std::string& dbusInterface =
749f3b04fd3SJason Ling                         inputSensorInterface.second;
750f3b04fd3SJason Ling                     const std::string& inputSensorPath =
751f3b04fd3SJason Ling                         inputSensorInterface.first;
752fb82a87dSJosh Lehan 
753fb82a87dSJosh Lehan                     // Setting timeout to 0 is intentional, as D-Bus passive
754fb82a87dSJosh Lehan                     // sensor updates are pushed in, not pulled by timer poll.
755fb82a87dSJosh Lehan                     // Setting ignoreDbusMinMax is intentional, as this
756fb82a87dSJosh Lehan                     // prevents normalization of values to [0.0, 1.0] range,
757fb82a87dSJosh Lehan                     // which would mess up the PID loop math.
758fb82a87dSJosh Lehan                     // All non-fan PID classes should be initialized this way.
759fb82a87dSJosh Lehan                     // As for why a fan should not use this code path, see
760fb82a87dSJosh Lehan                     // the ed1dafdf168def37c65bfb7a5efd18d9dbe04727 commit.
76123e22b90SJosh Lehan                     if ((pidClass == "temp") || (pidClass == "margin") ||
76223e22b90SJosh Lehan                         (pidClass == "power") || (pidClass == "powersum"))
763ed1dafdfSHarvey.Wu                     {
764f3b04fd3SJason Ling                         std::string inputSensorName =
765f3b04fd3SJason Ling                             getSensorNameFromPath(inputSensorPath);
766f3b04fd3SJason Ling                         auto& config = sensorConfig[inputSensorName];
767f3b04fd3SJason Ling                         inputSensorNames.push_back(inputSensorName);
768f3b04fd3SJason Ling                         config.type = pidClass;
769f3b04fd3SJason Ling                         config.readPath = inputSensorInterface.first;
7702642cb54SJames Feist                         config.timeout = 0;
7713433cb60SJames Feist                         config.ignoreDbusMinMax = true;
7728f73ad76SAlex.Song                         config.unavailableAsFailed = unavailableAsFailed;
77350fdfe39SJames Feist                     }
774fb82a87dSJosh Lehan 
775f3b04fd3SJason Ling                     if (dbusInterface != sensorInterface)
776f3b04fd3SJason Ling                     {
777f3b04fd3SJason Ling                         /* all expected inputs in the configuration are expected
778f3b04fd3SJason Ling                          * to be sensor interfaces
779f3b04fd3SJason Ling                          */
780f3b04fd3SJason Ling                         throw std::runtime_error(
781f3b04fd3SJason Ling                             "sensor at dbus path [" + inputSensorPath +
782f3b04fd3SJason Ling                             "] has an interface [" + dbusInterface +
783f3b04fd3SJason Ling                             "] that does not match the expected interface of " +
784f3b04fd3SJason Ling                             sensorInterface);
78550fdfe39SJames Feist                     }
78650fdfe39SJames Feist                 }
7871738e2a2SJames Feist 
7883f0f7bc3SJosh Lehan                 // MissingIsAcceptable same postprocessing as Inputs
7893f0f7bc3SJosh Lehan                 missingAcceptableSensorNames.clear();
7903f0f7bc3SJosh Lehan                 for (const SensorInterfaceType&
7913f0f7bc3SJosh Lehan                          missingAcceptableSensorInterface :
7923f0f7bc3SJosh Lehan                      missingAcceptableSensorInterfaces)
7933f0f7bc3SJosh Lehan                 {
7943f0f7bc3SJosh Lehan                     const std::string& dbusInterface =
7953f0f7bc3SJosh Lehan                         missingAcceptableSensorInterface.second;
7963f0f7bc3SJosh Lehan                     const std::string& missingAcceptableSensorPath =
7973f0f7bc3SJosh Lehan                         missingAcceptableSensorInterface.first;
7983f0f7bc3SJosh Lehan 
7993f0f7bc3SJosh Lehan                     std::string missingAcceptableSensorName =
8003f0f7bc3SJosh Lehan                         getSensorNameFromPath(missingAcceptableSensorPath);
8013f0f7bc3SJosh Lehan                     missingAcceptableSensorNames.push_back(
8023f0f7bc3SJosh Lehan                         missingAcceptableSensorName);
8033f0f7bc3SJosh Lehan 
8043f0f7bc3SJosh Lehan                     if (dbusInterface != sensorInterface)
8053f0f7bc3SJosh Lehan                     {
8063f0f7bc3SJosh Lehan                         /* MissingIsAcceptable same error checking as Inputs
8073f0f7bc3SJosh Lehan                          */
8083f0f7bc3SJosh Lehan                         throw std::runtime_error(
8093f0f7bc3SJosh Lehan                             "sensor at dbus path [" +
8103f0f7bc3SJosh Lehan                             missingAcceptableSensorPath +
8113f0f7bc3SJosh Lehan                             "] has an interface [" + dbusInterface +
8123f0f7bc3SJosh Lehan                             "] that does not match the expected interface of " +
8133f0f7bc3SJosh Lehan                             sensorInterface);
8143f0f7bc3SJosh Lehan                     }
8153f0f7bc3SJosh Lehan                 }
8163f0f7bc3SJosh Lehan 
817f3b04fd3SJason Ling                 /* fan pids need to pair up tach sensors with their pwm
818f3b04fd3SJason Ling                  * counterparts
819f3b04fd3SJason Ling                  */
820f3b04fd3SJason Ling                 if (pidClass == "fan")
821f3b04fd3SJason Ling                 {
822f3b04fd3SJason Ling                     /* If a PID is a fan there should be either
823f3b04fd3SJason Ling                      * (1) one output(pwm) per input(tach)
824f3b04fd3SJason Ling                      * OR
825f3b04fd3SJason Ling                      * (2) one putput(pwm) for all inputs(tach)
826f3b04fd3SJason Ling                      * everything else indicates a bad configuration.
827f3b04fd3SJason Ling                      */
828f3b04fd3SJason Ling                     bool singlePwm = false;
829f3b04fd3SJason Ling                     if (outputSensorInterfaces.size() == 1)
830f3b04fd3SJason Ling                     {
831f3b04fd3SJason Ling                         /* one pwm, set write paths for all fan sensors to it */
832f3b04fd3SJason Ling                         singlePwm = true;
833f3b04fd3SJason Ling                     }
834f3b04fd3SJason Ling                     else if (inputSensorInterfaces.size() ==
835f3b04fd3SJason Ling                              outputSensorInterfaces.size())
836f3b04fd3SJason Ling                     {
837f3b04fd3SJason Ling                         /* one to one mapping, each fan sensor gets its own pwm
838f3b04fd3SJason Ling                          * control */
839f3b04fd3SJason Ling                         singlePwm = false;
840f3b04fd3SJason Ling                     }
841f3b04fd3SJason Ling                     else
842f3b04fd3SJason Ling                     {
843f3b04fd3SJason Ling                         throw std::runtime_error(
844f3b04fd3SJason Ling                             "fan PID has invalid number of Outputs");
845f3b04fd3SJason Ling                     }
846f3b04fd3SJason Ling                     std::string fanSensorName;
847f3b04fd3SJason Ling                     std::string pwmPath;
848f3b04fd3SJason Ling                     std::string pwmInterface;
849ed1dafdfSHarvey.Wu                     std::string pwmSensorName;
850f3b04fd3SJason Ling                     if (singlePwm)
851f3b04fd3SJason Ling                     {
852f3b04fd3SJason Ling                         /* if just a single output(pwm) is provided then use
853f3b04fd3SJason Ling                          * that pwm control path for all the fan sensor write
854f3b04fd3SJason Ling                          * path configs
855f3b04fd3SJason Ling                          */
856f3b04fd3SJason Ling                         pwmPath = outputSensorInterfaces.at(0).first;
857f3b04fd3SJason Ling                         pwmInterface = outputSensorInterfaces.at(0).second;
858f3b04fd3SJason Ling                     }
859f3b04fd3SJason Ling                     for (uint32_t idx = 0; idx < inputSensorInterfaces.size();
860f3b04fd3SJason Ling                          idx++)
861f3b04fd3SJason Ling                     {
862f3b04fd3SJason Ling                         if (!singlePwm)
863f3b04fd3SJason Ling                         {
864f3b04fd3SJason Ling                             pwmPath = outputSensorInterfaces.at(idx).first;
865f3b04fd3SJason Ling                             pwmInterface =
866f3b04fd3SJason Ling                                 outputSensorInterfaces.at(idx).second;
867f3b04fd3SJason Ling                         }
8680911bfe7SPatrick Venture                         if (defaultPwmInterface != pwmInterface)
869f3b04fd3SJason Ling                         {
870f3b04fd3SJason Ling                             throw std::runtime_error(
871f3b04fd3SJason Ling                                 "fan pwm control at dbus path [" + pwmPath +
872f3b04fd3SJason Ling                                 "] has an interface [" + pwmInterface +
873f3b04fd3SJason Ling                                 "] that does not match the expected interface "
874f3b04fd3SJason Ling                                 "of " +
8750911bfe7SPatrick Venture                                 defaultPwmInterface);
876f3b04fd3SJason Ling                         }
877f3b04fd3SJason Ling                         const std::string& fanPath =
878f3b04fd3SJason Ling                             inputSensorInterfaces.at(idx).first;
879f3b04fd3SJason Ling                         fanSensorName = getSensorNameFromPath(fanPath);
880ed1dafdfSHarvey.Wu                         pwmSensorName = getSensorNameFromPath(pwmPath);
881ed1dafdfSHarvey.Wu                         std::string fanPwmIndex = fanSensorName + pwmSensorName;
882ed1dafdfSHarvey.Wu                         inputSensorNames.push_back(fanPwmIndex);
883ed1dafdfSHarvey.Wu                         auto& fanConfig = sensorConfig[fanPwmIndex];
884ed1dafdfSHarvey.Wu                         fanConfig.type = pidClass;
885ed1dafdfSHarvey.Wu                         fanConfig.readPath = fanPath;
886f3b04fd3SJason Ling                         fanConfig.writePath = pwmPath;
88750fdfe39SJames Feist                         // todo: un-hardcode this if there are fans with
88850fdfe39SJames Feist                         // different ranges
889f3b04fd3SJason Ling                         fanConfig.max = 255;
890f3b04fd3SJason Ling                         fanConfig.min = 0;
89150fdfe39SJames Feist                     }
89250fdfe39SJames Feist                 }
89311d243dfSJames Feist                 // if the sensors aren't available in the current state, don't
89411d243dfSJames Feist                 // add them to the configuration.
895f3b04fd3SJason Ling                 if (inputSensorNames.empty())
89611d243dfSJames Feist                 {
89711d243dfSJames Feist                     continue;
89811d243dfSJames Feist                 }
89911d243dfSJames Feist 
9005ec20270SJames Feist                 std::string offsetType;
9015ec20270SJames Feist 
9025ec20270SJames Feist                 // SetPointOffset is a threshold value to pull from the sensor
9035ec20270SJames Feist                 // to apply an offset. For upper thresholds this means the
9045ec20270SJames Feist                 // setpoint is usually negative.
9055ec20270SJames Feist                 auto findSetpointOffset = base.find("SetPointOffset");
9065ec20270SJames Feist                 if (findSetpointOffset != base.end())
9075ec20270SJames Feist                 {
9085ec20270SJames Feist                     offsetType =
9095ec20270SJames Feist                         std::get<std::string>(findSetpointOffset->second);
9105ec20270SJames Feist                     if (std::find(thresholds::types.begin(),
9115ec20270SJames Feist                                   thresholds::types.end(),
9125ec20270SJames Feist                                   offsetType) == thresholds::types.end())
9135ec20270SJames Feist                     {
9145ec20270SJames Feist                         throw std::runtime_error("Unsupported type: " +
9155ec20270SJames Feist                                                  offsetType);
9165ec20270SJames Feist                     }
9175ec20270SJames Feist                 }
9185ec20270SJames Feist 
91931058fd3SJosh Lehan                 std::vector<double> inputTempToMargin;
92031058fd3SJosh Lehan 
92131058fd3SJosh Lehan                 auto findTempToMargin = base.find("TempToMargin");
92231058fd3SJosh Lehan                 if (findTempToMargin != base.end())
92331058fd3SJosh Lehan                 {
92431058fd3SJosh Lehan                     inputTempToMargin =
92531058fd3SJosh Lehan                         std::get<std::vector<double>>(findTempToMargin->second);
92631058fd3SJosh Lehan                 }
92731058fd3SJosh Lehan 
92831058fd3SJosh Lehan                 std::vector<pid_control::conf::SensorInput> sensorInputs =
9293f0f7bc3SJosh Lehan                     spliceInputs(inputSensorNames, inputTempToMargin,
9303f0f7bc3SJosh Lehan                                  missingAcceptableSensorNames);
93131058fd3SJosh Lehan 
9325ec20270SJames Feist                 if (offsetType.empty())
9335ec20270SJames Feist                 {
9347c6d35d5Sykchiu                     conf::ControllerInfo& info = conf[pidName];
93531058fd3SJosh Lehan                     info.inputs = std::move(sensorInputs);
9367382318fSPatrick Venture                     populatePidInfo(bus, base, info, nullptr, sensorConfig);
9377136a5aeSJames Feist                 }
9387136a5aeSJames Feist                 else
9397136a5aeSJames Feist                 {
9405ec20270SJames Feist                     // we have to split up the inputs, as in practice t-control
9415ec20270SJames Feist                     // values will differ, making setpoints differ
94231058fd3SJosh Lehan                     for (const pid_control::conf::SensorInput& input :
94331058fd3SJosh Lehan                          sensorInputs)
944572c43daSJames Feist                     {
94531058fd3SJosh Lehan                         conf::ControllerInfo& info = conf[input.name];
9465ec20270SJames Feist                         info.inputs.emplace_back(input);
9477382318fSPatrick Venture                         populatePidInfo(bus, base, info, &offsetType,
9487382318fSPatrick Venture                                         sensorConfig);
949572c43daSJames Feist                     }
950572c43daSJames Feist                 }
9517136a5aeSJames Feist             }
9528c3c51eeSJames Feist         }
95322c257abSJames Feist         auto findStepwise =
95422c257abSJames Feist             configuration.second.find(stepwiseConfigurationInterface);
95522c257abSJames Feist         if (findStepwise != configuration.second.end())
95622c257abSJames Feist         {
95722c257abSJames Feist             const auto& base = findStepwise->second;
9587c6d35d5Sykchiu             const std::string pidName =
9597c6d35d5Sykchiu                 sensorNameToDbusName(std::get<std::string>(base.at("Name")));
96022c257abSJames Feist             const std::vector<std::string>& zones =
9611f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
96222c257abSJames Feist             for (const std::string& zone : zones)
96322c257abSJames Feist             {
964998fbe67SJosh Lehan                 auto index = getZoneIndex(zone, foundZones);
965998fbe67SJosh Lehan 
966f81f2886SJames Feist                 conf::PIDConf& conf = zoneConfig[index];
96750fdfe39SJames Feist 
96850fdfe39SJames Feist                 std::vector<std::string> inputs;
9693f0f7bc3SJosh Lehan                 std::vector<std::string> missingAcceptableSensors;
9703f0f7bc3SJosh Lehan                 std::vector<std::string> missingAcceptableSensorNames;
97150fdfe39SJames Feist                 std::vector<std::string> sensorNames =
9721f802f5eSJames Feist                     std::get<std::vector<std::string>>(base.at("Inputs"));
97350fdfe39SJames Feist 
9743f0f7bc3SJosh Lehan                 auto findMissingAcceptable = base.find("MissingIsAcceptable");
9753f0f7bc3SJosh Lehan                 if (findMissingAcceptable != base.end())
9763f0f7bc3SJosh Lehan                 {
9773f0f7bc3SJosh Lehan                     missingAcceptableSensorNames =
9783f0f7bc3SJosh Lehan                         std::get<std::vector<std::string>>(
9793f0f7bc3SJosh Lehan                             findMissingAcceptable->second);
9803f0f7bc3SJosh Lehan                 }
9813f0f7bc3SJosh Lehan 
9828f73ad76SAlex.Song                 bool unavailableAsFailed = true;
9838f73ad76SAlex.Song                 auto findUnavailableAsFailed =
9848f73ad76SAlex.Song                     base.find("InputUnavailableAsFailed");
9858f73ad76SAlex.Song                 if (findUnavailableAsFailed != base.end())
9868f73ad76SAlex.Song                 {
9878f73ad76SAlex.Song                     unavailableAsFailed =
9888f73ad76SAlex.Song                         std::get<bool>(findUnavailableAsFailed->second);
9898f73ad76SAlex.Song                 }
9908f73ad76SAlex.Song 
9911738e2a2SJames Feist                 bool sensorFound = false;
99250fdfe39SJames Feist                 for (const std::string& sensorName : sensorNames)
99350fdfe39SJames Feist                 {
9941738e2a2SJames Feist                     std::vector<std::pair<std::string, std::string>>
9951738e2a2SJames Feist                         sensorPathIfacePairs;
996f3b04fd3SJason Ling                     if (!findSensors(sensors, sensorNameToDbusName(sensorName),
997f3b04fd3SJason Ling                                      sensorPathIfacePairs))
99850fdfe39SJames Feist                     {
99950fdfe39SJames Feist                         break;
100050fdfe39SJames Feist                     }
100150fdfe39SJames Feist 
10021738e2a2SJames Feist                     for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
10031738e2a2SJames Feist                     {
10041738e2a2SJames Feist                         std::string shortName =
10053f0f7bc3SJosh Lehan                             getSensorNameFromPath(sensorPathIfacePair.first);
10061738e2a2SJames Feist 
10071738e2a2SJames Feist                         inputs.push_back(shortName);
10081738e2a2SJames Feist                         auto& config = sensorConfig[shortName];
100969c51061SPatrick Venture                         config.readPath = sensorPathIfacePair.first;
101050fdfe39SJames Feist                         config.type = "temp";
10113660b388SJames Feist                         config.ignoreDbusMinMax = true;
10128f73ad76SAlex.Song                         config.unavailableAsFailed = unavailableAsFailed;
101350fdfe39SJames Feist                         // todo: maybe un-hardcode this if we run into slower
101450fdfe39SJames Feist                         // timeouts with sensors
101550fdfe39SJames Feist 
10162642cb54SJames Feist                         config.timeout = 0;
10171738e2a2SJames Feist                         sensorFound = true;
10181738e2a2SJames Feist                     }
101950fdfe39SJames Feist                 }
102050fdfe39SJames Feist                 if (!sensorFound)
102150fdfe39SJames Feist                 {
102250fdfe39SJames Feist                     continue;
102350fdfe39SJames Feist                 }
10243f0f7bc3SJosh Lehan 
10253f0f7bc3SJosh Lehan                 // MissingIsAcceptable same postprocessing as Inputs
10263f0f7bc3SJosh Lehan                 for (const std::string& missingAcceptableSensorName :
10273f0f7bc3SJosh Lehan                      missingAcceptableSensorNames)
10283f0f7bc3SJosh Lehan                 {
10293f0f7bc3SJosh Lehan                     std::vector<std::pair<std::string, std::string>>
10303f0f7bc3SJosh Lehan                         sensorPathIfacePairs;
10313f0f7bc3SJosh Lehan                     if (!findSensors(
10323f0f7bc3SJosh Lehan                             sensors,
10333f0f7bc3SJosh Lehan                             sensorNameToDbusName(missingAcceptableSensorName),
10343f0f7bc3SJosh Lehan                             sensorPathIfacePairs))
10353f0f7bc3SJosh Lehan                     {
10363f0f7bc3SJosh Lehan                         break;
10373f0f7bc3SJosh Lehan                     }
10383f0f7bc3SJosh Lehan 
10393f0f7bc3SJosh Lehan                     for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
10403f0f7bc3SJosh Lehan                     {
10413f0f7bc3SJosh Lehan                         std::string shortName =
10423f0f7bc3SJosh Lehan                             getSensorNameFromPath(sensorPathIfacePair.first);
10433f0f7bc3SJosh Lehan 
10443f0f7bc3SJosh Lehan                         missingAcceptableSensors.push_back(shortName);
10453f0f7bc3SJosh Lehan                     }
10463f0f7bc3SJosh Lehan                 }
10473f0f7bc3SJosh Lehan 
10487c6d35d5Sykchiu                 conf::ControllerInfo& info = conf[pidName];
104931058fd3SJosh Lehan 
105031058fd3SJosh Lehan                 std::vector<double> inputTempToMargin;
105131058fd3SJosh Lehan 
105231058fd3SJosh Lehan                 auto findTempToMargin = base.find("TempToMargin");
105331058fd3SJosh Lehan                 if (findTempToMargin != base.end())
105431058fd3SJosh Lehan                 {
105531058fd3SJosh Lehan                     inputTempToMargin =
105631058fd3SJosh Lehan                         std::get<std::vector<double>>(findTempToMargin->second);
105731058fd3SJosh Lehan                 }
105831058fd3SJosh Lehan 
10593f0f7bc3SJosh Lehan                 info.inputs = spliceInputs(inputs, inputTempToMargin,
10603f0f7bc3SJosh Lehan                                            missingAcceptableSensors);
106150fdfe39SJames Feist 
106222c257abSJames Feist                 info.type = "stepwise";
106322c257abSJames Feist                 info.stepwiseInfo.ts = 1.0; // currently unused
10643dfaafdaSJames Feist                 info.stepwiseInfo.positiveHysteresis = 0.0;
10653dfaafdaSJames Feist                 info.stepwiseInfo.negativeHysteresis = 0.0;
1066608304daSJames Feist                 std::string subtype = std::get<std::string>(base.at("Class"));
1067608304daSJames Feist 
1068608304daSJames Feist                 info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
10693dfaafdaSJames Feist                 auto findPosHyst = base.find("PositiveHysteresis");
10703dfaafdaSJames Feist                 auto findNegHyst = base.find("NegativeHysteresis");
10713dfaafdaSJames Feist                 if (findPosHyst != base.end())
10723dfaafdaSJames Feist                 {
10731f802f5eSJames Feist                     info.stepwiseInfo.positiveHysteresis = std::visit(
1074208abce8SJames Feist                         VariantToDoubleVisitor(), findPosHyst->second);
10753dfaafdaSJames Feist                 }
10763dfaafdaSJames Feist                 if (findNegHyst != base.end())
10773dfaafdaSJames Feist                 {
10785782ab81SJames Feist                     info.stepwiseInfo.negativeHysteresis = std::visit(
1079208abce8SJames Feist                         VariantToDoubleVisitor(), findNegHyst->second);
10803dfaafdaSJames Feist                 }
108122c257abSJames Feist                 std::vector<double> readings =
10821f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Reading"));
108322c257abSJames Feist                 if (readings.size() > ec::maxStepwisePoints)
108422c257abSJames Feist                 {
108522c257abSJames Feist                     throw std::invalid_argument("Too many stepwise points.");
108622c257abSJames Feist                 }
108722c257abSJames Feist                 if (readings.empty())
108822c257abSJames Feist                 {
108922c257abSJames Feist                     throw std::invalid_argument(
109022c257abSJames Feist                         "Must have one stepwise point.");
109122c257abSJames Feist                 }
109222c257abSJames Feist                 std::copy(readings.begin(), readings.end(),
109322c257abSJames Feist                           info.stepwiseInfo.reading);
109422c257abSJames Feist                 if (readings.size() < ec::maxStepwisePoints)
109522c257abSJames Feist                 {
109622c257abSJames Feist                     info.stepwiseInfo.reading[readings.size()] =
10975f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
109822c257abSJames Feist                 }
109922c257abSJames Feist                 std::vector<double> outputs =
11001f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Output"));
110122c257abSJames Feist                 if (readings.size() != outputs.size())
110222c257abSJames Feist                 {
110322c257abSJames Feist                     throw std::invalid_argument(
110422c257abSJames Feist                         "Outputs size must match readings");
110522c257abSJames Feist                 }
110622c257abSJames Feist                 std::copy(outputs.begin(), outputs.end(),
110722c257abSJames Feist                           info.stepwiseInfo.output);
110822c257abSJames Feist                 if (outputs.size() < ec::maxStepwisePoints)
110922c257abSJames Feist                 {
111022c257abSJames Feist                     info.stepwiseInfo.output[outputs.size()] =
11115f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
111222c257abSJames Feist                 }
111322c257abSJames Feist             }
111422c257abSJames Feist         }
111522c257abSJames Feist     }
111639199b4dSPatrick Venture     if constexpr (pid_control::conf::DEBUG)
11177136a5aeSJames Feist     {
111839199b4dSPatrick Venture         debugPrint(sensorConfig, zoneConfig, zoneDetailsConfig);
11197136a5aeSJames Feist     }
1120c959c429SJames Feist     if (zoneConfig.empty() || zoneDetailsConfig.empty())
112150fdfe39SJames Feist     {
11221fe08952SJames Feist         std::cerr
11231fe08952SJames Feist             << "No fan zones, application pausing until new configuration\n";
11241fe08952SJames Feist         return false;
112550fdfe39SJames Feist     }
11261fe08952SJames Feist     return true;
11277136a5aeSJames Feist }
1128a076487aSPatrick Venture 
11297136a5aeSJames Feist } // namespace dbus_configuration
1130a076487aSPatrick Venture } // namespace pid_control
1131