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 
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 
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 
84b228bc30SPatrick Williams std::vector<std::string> getSelectedProfiles(sdbusplus::bus_t& bus)
85f0096a0cSJames Feist {
86f0096a0cSJames Feist     std::vector<std::string> ret;
87f0096a0cSJames Feist     auto mapper =
88f0096a0cSJames Feist         bus.new_method_call("xyz.openbmc_project.ObjectMapper",
89f0096a0cSJames Feist                             "/xyz/openbmc_project/object_mapper",
90f0096a0cSJames Feist                             "xyz.openbmc_project.ObjectMapper", "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 
152991ebd80SJames Feist int eventHandler(sd_bus_message* m, void* context, sd_bus_error*)
1537136a5aeSJames Feist {
1541fe08952SJames Feist 
155991ebd80SJames Feist     if (context == nullptr || m == nullptr)
1561fe08952SJames Feist     {
1571fe08952SJames Feist         throw std::runtime_error("Invalid match");
1581fe08952SJames Feist     }
159991ebd80SJames Feist 
160991ebd80SJames Feist     // we skip associations because the mapper populates these, not the sensors
161991ebd80SJames Feist     const std::array<const char*, 1> skipList = {
162991ebd80SJames Feist         "xyz.openbmc_project.Association"};
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         }
187991ebd80SJames Feist     }
188991ebd80SJames Feist 
1891fe08952SJames Feist     boost::asio::steady_timer* timer =
1901fe08952SJames Feist         static_cast<boost::asio::steady_timer*>(context);
1911fe08952SJames Feist 
1921fe08952SJames Feist     // do a brief sleep as we tend to get a bunch of these events at
1931fe08952SJames Feist     // once
1941fe08952SJames Feist     timer->expires_after(std::chrono::seconds(2));
1951fe08952SJames Feist     timer->async_wait([](const boost::system::error_code ec) {
1961fe08952SJames Feist         if (ec == boost::asio::error::operation_aborted)
1971fe08952SJames Feist         {
1981fe08952SJames Feist             /* another timer started*/
1991fe08952SJames Feist             return;
2001fe08952SJames Feist         }
2011fe08952SJames Feist 
2021fe08952SJames Feist         std::cout << "New configuration detected, reloading\n.";
203298a95cbSYong Li         tryRestartControlLoops();
2041fe08952SJames Feist     });
2051fe08952SJames Feist 
2061fe08952SJames Feist     return 1;
2071fe08952SJames Feist }
2081fe08952SJames Feist 
209b228bc30SPatrick Williams void createMatches(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer)
2101fe08952SJames Feist {
2111fe08952SJames Feist     // this is a list because the matches can't be moved
212b228bc30SPatrick Williams     static std::list<sdbusplus::bus::match_t> matches;
2131fe08952SJames Feist 
2143987c8b4SJames Feist     const std::array<std::string, 4> interfaces = {
2153987c8b4SJames Feist         thermalControlIface, pidConfigurationInterface,
2163987c8b4SJames Feist         pidZoneConfigurationInterface, stepwiseConfigurationInterface};
2171fe08952SJames Feist 
2181fe08952SJames Feist     // this list only needs to be created once
2191fe08952SJames Feist     if (!matches.empty())
2201fe08952SJames Feist     {
2211fe08952SJames Feist         return;
2221fe08952SJames Feist     }
2231fe08952SJames Feist 
2241fe08952SJames Feist     // we restart when the configuration changes or there are new sensors
2251fe08952SJames Feist     for (const auto& interface : interfaces)
2261fe08952SJames Feist     {
2271fe08952SJames Feist         matches.emplace_back(
2281fe08952SJames Feist             bus,
2291fe08952SJames Feist             "type='signal',member='PropertiesChanged',arg0namespace='" +
2301fe08952SJames Feist                 interface + "'",
2311fe08952SJames Feist             eventHandler, &timer);
2321fe08952SJames Feist     }
2331fe08952SJames Feist     matches.emplace_back(
2341fe08952SJames Feist         bus,
2351fe08952SJames Feist         "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
2361fe08952SJames Feist         "sensors/'",
2371fe08952SJames Feist         eventHandler, &timer);
2381fe08952SJames Feist }
2391fe08952SJames Feist 
2406fc301fbSJason Ling /**
2416fc301fbSJason Ling  * retrieve an attribute from the pid configuration map
2426fc301fbSJason Ling  * @param[in] base - the PID configuration map, keys are the attributes and
2436fc301fbSJason Ling  * value is the variant associated with that attribute.
2446fc301fbSJason Ling  * @param attributeName - the name of the attribute
2456fc301fbSJason Ling  * @return a variant holding the value associated with a key
2466fc301fbSJason Ling  * @throw runtime_error : attributeName is not in base
2476fc301fbSJason Ling  */
2486fc301fbSJason Ling inline DbusVariantType getPIDAttribute(
2496fc301fbSJason Ling     const std::unordered_map<std::string, DbusVariantType>& base,
2506fc301fbSJason Ling     const std::string& attributeName)
2516fc301fbSJason Ling {
2526fc301fbSJason Ling     auto search = base.find(attributeName);
2536fc301fbSJason Ling     if (search == base.end())
2546fc301fbSJason Ling     {
2556fc301fbSJason Ling         throw std::runtime_error("missing attribute " + attributeName);
2566fc301fbSJason Ling     }
2576fc301fbSJason Ling     return search->second;
2586fc301fbSJason Ling }
2596fc301fbSJason Ling 
2605ec20270SJames Feist void populatePidInfo(
261*a1ae4fa1SHarvey.Wu     [[maybe_unused]] sdbusplus::bus_t& bus,
2625ec20270SJames Feist     const std::unordered_map<std::string, DbusVariantType>& base,
2631df9e879SPatrick Venture     conf::ControllerInfo& info, const std::string* thresholdProperty,
2647382318fSPatrick Venture     const std::map<std::string, conf::SensorConfig>& sensorConfig)
2655ec20270SJames Feist {
2666fc301fbSJason Ling     info.type = std::get<std::string>(getPIDAttribute(base, "Class"));
2675ec20270SJames Feist     if (info.type == "fan")
2685ec20270SJames Feist     {
2695ec20270SJames Feist         info.setpoint = 0;
2705ec20270SJames Feist     }
2715ec20270SJames Feist     else
2725ec20270SJames Feist     {
2736fc301fbSJason Ling         info.setpoint = std::visit(VariantToDoubleVisitor(),
2746fc301fbSJason Ling                                    getPIDAttribute(base, "SetPoint"));
2755ec20270SJames Feist     }
2765ec20270SJames Feist 
2775ec20270SJames Feist     if (thresholdProperty != nullptr)
2785ec20270SJames Feist     {
2795ec20270SJames Feist         std::string interface;
2805ec20270SJames Feist         if (*thresholdProperty == "WarningHigh" ||
2815ec20270SJames Feist             *thresholdProperty == "WarningLow")
2825ec20270SJames Feist         {
2835ec20270SJames Feist             interface = thresholds::warningInterface;
2845ec20270SJames Feist         }
2855ec20270SJames Feist         else
2865ec20270SJames Feist         {
2875ec20270SJames Feist             interface = thresholds::criticalInterface;
2885ec20270SJames Feist         }
2897382318fSPatrick Venture         const std::string& path = sensorConfig.at(info.inputs.front()).readPath;
2905ec20270SJames Feist 
2918729eb98SPatrick Venture         DbusHelper helper(sdbusplus::bus::new_system());
2929b93692dSPatrick Venture         std::string service = helper.getService(interface, path);
2935ec20270SJames Feist         double reading = 0;
2945ec20270SJames Feist         try
2955ec20270SJames Feist         {
2969b93692dSPatrick Venture             helper.getProperty(service, path, interface, *thresholdProperty,
2979b93692dSPatrick Venture                                reading);
2985ec20270SJames Feist         }
299b228bc30SPatrick Williams         catch (const sdbusplus::exception_t& ex)
3005ec20270SJames Feist         {
3015ec20270SJames Feist             // unsupported threshold, leaving reading at 0
3025ec20270SJames Feist         }
3035ec20270SJames Feist 
3045ec20270SJames Feist         info.setpoint += reading;
3055ec20270SJames Feist     }
3065ec20270SJames Feist 
3075ec20270SJames Feist     info.pidInfo.ts = 1.0; // currently unused
3086fc301fbSJason Ling     info.pidInfo.proportionalCoeff = std::visit(
3096fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "PCoefficient"));
3106fc301fbSJason Ling     info.pidInfo.integralCoeff = std::visit(
3116fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ICoefficient"));
3126fc301fbSJason Ling     info.pidInfo.feedFwdOffset = std::visit(
3136fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "FFOffCoefficient"));
3146fc301fbSJason Ling     info.pidInfo.feedFwdGain = std::visit(
3156fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "FFGainCoefficient"));
3166fc301fbSJason Ling     info.pidInfo.integralLimit.max = std::visit(
3176fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMax"));
3186fc301fbSJason Ling     info.pidInfo.integralLimit.min = std::visit(
3196fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMin"));
3206fc301fbSJason Ling     info.pidInfo.outLim.max = std::visit(VariantToDoubleVisitor(),
3216fc301fbSJason Ling                                          getPIDAttribute(base, "OutLimitMax"));
3226fc301fbSJason Ling     info.pidInfo.outLim.min = std::visit(VariantToDoubleVisitor(),
3236fc301fbSJason Ling                                          getPIDAttribute(base, "OutLimitMin"));
3245ec20270SJames Feist     info.pidInfo.slewNeg =
3256fc301fbSJason Ling         std::visit(VariantToDoubleVisitor(), getPIDAttribute(base, "SlewNeg"));
3265ec20270SJames Feist     info.pidInfo.slewPos =
3276fc301fbSJason Ling         std::visit(VariantToDoubleVisitor(), getPIDAttribute(base, "SlewPos"));
3285ec20270SJames Feist     double negativeHysteresis = 0;
3295ec20270SJames Feist     double positiveHysteresis = 0;
3305ec20270SJames Feist 
3315ec20270SJames Feist     auto findNeg = base.find("NegativeHysteresis");
3325ec20270SJames Feist     auto findPos = base.find("PositiveHysteresis");
3335ec20270SJames Feist 
3345ec20270SJames Feist     if (findNeg != base.end())
3355ec20270SJames Feist     {
3365ec20270SJames Feist         negativeHysteresis =
3375ec20270SJames Feist             std::visit(VariantToDoubleVisitor(), findNeg->second);
3385ec20270SJames Feist     }
3395ec20270SJames Feist 
3405ec20270SJames Feist     if (findPos != base.end())
3415ec20270SJames Feist     {
3425ec20270SJames Feist         positiveHysteresis =
3435ec20270SJames Feist             std::visit(VariantToDoubleVisitor(), findPos->second);
3445ec20270SJames Feist     }
3455ec20270SJames Feist     info.pidInfo.negativeHysteresis = negativeHysteresis;
3465ec20270SJames Feist     info.pidInfo.positiveHysteresis = positiveHysteresis;
3475ec20270SJames Feist }
3485ec20270SJames Feist 
349b228bc30SPatrick Williams bool init(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer,
3507382318fSPatrick Venture           std::map<std::string, conf::SensorConfig>& sensorConfig,
3517382318fSPatrick Venture           std::map<int64_t, conf::PIDConf>& zoneConfig,
3527382318fSPatrick Venture           std::map<int64_t, conf::ZoneConfig>& zoneDetailsConfig)
3531fe08952SJames Feist {
3541fe08952SJames Feist 
3551fe08952SJames Feist     sensorConfig.clear();
3561fe08952SJames Feist     zoneConfig.clear();
3571fe08952SJames Feist     zoneDetailsConfig.clear();
3581fe08952SJames Feist 
3591fe08952SJames Feist     createMatches(bus, timer);
3601fe08952SJames Feist 
3617136a5aeSJames Feist     auto mapper =
3627136a5aeSJames Feist         bus.new_method_call("xyz.openbmc_project.ObjectMapper",
3637136a5aeSJames Feist                             "/xyz/openbmc_project/object_mapper",
3647136a5aeSJames Feist                             "xyz.openbmc_project.ObjectMapper", "GetSubTree");
36526e8c6a9SJames Feist     mapper.append("/", 0,
3660911bfe7SPatrick Venture                   std::array<const char*, 6>{
3670911bfe7SPatrick Venture                       objectManagerInterface, pidConfigurationInterface,
3687136a5aeSJames Feist                       pidZoneConfigurationInterface,
3690911bfe7SPatrick Venture                       stepwiseConfigurationInterface, sensorInterface,
3700911bfe7SPatrick Venture                       defaultPwmInterface});
37122c257abSJames Feist     std::unordered_map<
37222c257abSJames Feist         std::string, std::unordered_map<std::string, std::vector<std::string>>>
37322c257abSJames Feist         respData;
37422c257abSJames Feist     try
37522c257abSJames Feist     {
3767136a5aeSJames Feist         auto resp = bus.call(mapper);
3777136a5aeSJames Feist         resp.read(respData);
37822c257abSJames Feist     }
3790001ee02SPatrick Williams     catch (const sdbusplus::exception_t&)
38022c257abSJames Feist     {
38122c257abSJames Feist         // can't do anything without mapper call data
38222c257abSJames Feist         throw std::runtime_error("ObjectMapper Call Failure");
38322c257abSJames Feist     }
38422c257abSJames Feist 
3857136a5aeSJames Feist     if (respData.empty())
3867136a5aeSJames Feist     {
38722c257abSJames Feist         // can't do anything without mapper call data
3887136a5aeSJames Feist         throw std::runtime_error("No configuration data available from Mapper");
3897136a5aeSJames Feist     }
3907136a5aeSJames Feist     // create a map of pair of <has pid configuration, ObjectManager path>
3917136a5aeSJames Feist     std::unordered_map<std::string, std::pair<bool, std::string>> owners;
3927136a5aeSJames Feist     // and a map of <path, interface> for sensors
3937136a5aeSJames Feist     std::unordered_map<std::string, std::string> sensors;
3947136a5aeSJames Feist     for (const auto& objectPair : respData)
3957136a5aeSJames Feist     {
3967136a5aeSJames Feist         for (const auto& ownerPair : objectPair.second)
3977136a5aeSJames Feist         {
3987136a5aeSJames Feist             auto& owner = owners[ownerPair.first];
3997136a5aeSJames Feist             for (const std::string& interface : ownerPair.second)
4007136a5aeSJames Feist             {
4017136a5aeSJames Feist 
4027136a5aeSJames Feist                 if (interface == objectManagerInterface)
4037136a5aeSJames Feist                 {
4047136a5aeSJames Feist                     owner.second = objectPair.first;
4057136a5aeSJames Feist                 }
4067136a5aeSJames Feist                 if (interface == pidConfigurationInterface ||
40722c257abSJames Feist                     interface == pidZoneConfigurationInterface ||
40822c257abSJames Feist                     interface == stepwiseConfigurationInterface)
4097136a5aeSJames Feist                 {
4107136a5aeSJames Feist                     owner.first = true;
4117136a5aeSJames Feist                 }
4120911bfe7SPatrick Venture                 if (interface == sensorInterface ||
4130911bfe7SPatrick Venture                     interface == defaultPwmInterface)
4147136a5aeSJames Feist                 {
4157136a5aeSJames Feist                     // we're not interested in pwm sensors, just pwm control
4167136a5aeSJames Feist                     if (interface == sensorInterface &&
4177136a5aeSJames Feist                         objectPair.first.find("pwm") != std::string::npos)
4187136a5aeSJames Feist                     {
4197136a5aeSJames Feist                         continue;
4207136a5aeSJames Feist                     }
4217136a5aeSJames Feist                     sensors[objectPair.first] = interface;
4227136a5aeSJames Feist                 }
4237136a5aeSJames Feist             }
4247136a5aeSJames Feist         }
4257136a5aeSJames Feist     }
4267136a5aeSJames Feist     ManagedObjectType configurations;
4277136a5aeSJames Feist     for (const auto& owner : owners)
4287136a5aeSJames Feist     {
4297136a5aeSJames Feist         // skip if no pid configuration (means probably a sensor)
4307136a5aeSJames Feist         if (!owner.second.first)
4317136a5aeSJames Feist         {
4327136a5aeSJames Feist             continue;
4337136a5aeSJames Feist         }
4347136a5aeSJames Feist         auto endpoint = bus.new_method_call(
4357136a5aeSJames Feist             owner.first.c_str(), owner.second.second.c_str(),
4367136a5aeSJames Feist             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
43722c257abSJames Feist         ManagedObjectType configuration;
43822c257abSJames Feist         try
43922c257abSJames Feist         {
4407136a5aeSJames Feist             auto responce = bus.call(endpoint);
4417136a5aeSJames Feist             responce.read(configuration);
44222c257abSJames Feist         }
4430001ee02SPatrick Williams         catch (const sdbusplus::exception_t&)
44422c257abSJames Feist         {
44522c257abSJames Feist             // this shouldn't happen, probably means daemon crashed
44622c257abSJames Feist             throw std::runtime_error("Error getting managed objects from " +
44722c257abSJames Feist                                      owner.first);
44822c257abSJames Feist         }
44922c257abSJames Feist 
4507136a5aeSJames Feist         for (auto& pathPair : configuration)
4517136a5aeSJames Feist         {
4527136a5aeSJames Feist             if (pathPair.second.find(pidConfigurationInterface) !=
4537136a5aeSJames Feist                     pathPair.second.end() ||
4547136a5aeSJames Feist                 pathPair.second.find(pidZoneConfigurationInterface) !=
45522c257abSJames Feist                     pathPair.second.end() ||
45622c257abSJames Feist                 pathPair.second.find(stepwiseConfigurationInterface) !=
4577136a5aeSJames Feist                     pathPair.second.end())
4587136a5aeSJames Feist             {
4597136a5aeSJames Feist                 configurations.emplace(pathPair);
4607136a5aeSJames Feist             }
461f0096a0cSJames Feist         }
462f0096a0cSJames Feist     }
463f0096a0cSJames Feist 
464f0096a0cSJames Feist     // remove controllers from config that aren't in the current profile(s)
465f0096a0cSJames Feist     std::vector<std::string> selectedProfiles = getSelectedProfiles(bus);
466f0096a0cSJames Feist     if (selectedProfiles.size())
467f0096a0cSJames Feist     {
4683987c8b4SJames Feist         for (auto pathIt = configurations.begin();
4693987c8b4SJames Feist              pathIt != configurations.end();)
470f0096a0cSJames Feist         {
4713987c8b4SJames Feist             for (auto confIt = pathIt->second.begin();
4723987c8b4SJames Feist                  confIt != pathIt->second.end();)
4733987c8b4SJames Feist             {
4743987c8b4SJames Feist                 auto profilesFind = confIt->second.find("Profiles");
4753987c8b4SJames Feist                 if (profilesFind == confIt->second.end())
4763987c8b4SJames Feist                 {
4773987c8b4SJames Feist                     confIt++;
4783987c8b4SJames Feist                     continue; // if no profiles selected, apply always
4793987c8b4SJames Feist                 }
4803987c8b4SJames Feist                 auto profiles =
4813987c8b4SJames Feist                     std::get<std::vector<std::string>>(profilesFind->second);
4823987c8b4SJames Feist                 if (profiles.empty())
4833987c8b4SJames Feist                 {
4843987c8b4SJames Feist                     confIt++;
4853987c8b4SJames Feist                     continue;
486f0096a0cSJames Feist                 }
487f0096a0cSJames Feist 
4883987c8b4SJames Feist                 bool found = false;
4893987c8b4SJames Feist                 for (const std::string& profile : profiles)
490f0096a0cSJames Feist                 {
4913987c8b4SJames Feist                     if (std::find(selectedProfiles.begin(),
4923987c8b4SJames Feist                                   selectedProfiles.end(),
4933987c8b4SJames Feist                                   profile) != selectedProfiles.end())
494f0096a0cSJames Feist                     {
4953987c8b4SJames Feist                         found = true;
4963987c8b4SJames Feist                         break;
4973987c8b4SJames Feist                     }
4983987c8b4SJames Feist                 }
4993987c8b4SJames Feist                 if (found)
5003987c8b4SJames Feist                 {
5013987c8b4SJames Feist                     confIt++;
502f0096a0cSJames Feist                 }
503f0096a0cSJames Feist                 else
504f0096a0cSJames Feist                 {
5053987c8b4SJames Feist                     confIt = pathIt->second.erase(confIt);
506f0096a0cSJames Feist                 }
507f0096a0cSJames Feist             }
5083987c8b4SJames Feist             if (pathIt->second.empty())
509f0096a0cSJames Feist             {
5103987c8b4SJames Feist                 pathIt = configurations.erase(pathIt);
511f0096a0cSJames Feist             }
512f0096a0cSJames Feist             else
513f0096a0cSJames Feist             {
5143987c8b4SJames Feist                 pathIt++;
515f0096a0cSJames Feist             }
5167136a5aeSJames Feist         }
5177136a5aeSJames Feist     }
5188c3c51eeSJames Feist 
519998fbe67SJosh Lehan     // On D-Bus, although not necessary,
520998fbe67SJosh Lehan     // having the "zoneID" field can still be useful,
521998fbe67SJosh Lehan     // as it is used for diagnostic messages,
522998fbe67SJosh Lehan     // logging file names, and so on.
523998fbe67SJosh Lehan     // Accept optional "ZoneIndex" parameter to explicitly specify.
524998fbe67SJosh Lehan     // If not present, or not unique, auto-assign index,
525998fbe67SJosh Lehan     // using 0-based numbering, ensuring uniqueness.
526998fbe67SJosh Lehan     std::map<std::string, int64_t> foundZones;
5277136a5aeSJames Feist     for (const auto& configuration : configurations)
5287136a5aeSJames Feist     {
5297136a5aeSJames Feist         auto findZone =
5307136a5aeSJames Feist             configuration.second.find(pidZoneConfigurationInterface);
5317136a5aeSJames Feist         if (findZone != configuration.second.end())
5327136a5aeSJames Feist         {
5337136a5aeSJames Feist             const auto& zone = findZone->second;
534ffd418bbSJames Feist 
5351f802f5eSJames Feist             const std::string& name = std::get<std::string>(zone.at("Name"));
536998fbe67SJosh Lehan 
537998fbe67SJosh Lehan             auto findZoneIndex = zone.find("ZoneIndex");
538998fbe67SJosh Lehan             if (findZoneIndex == zone.end())
539998fbe67SJosh Lehan             {
540998fbe67SJosh Lehan                 continue;
541998fbe67SJosh Lehan             }
542998fbe67SJosh Lehan 
543998fbe67SJosh Lehan             auto ptrZoneIndex = std::get_if<double>(&(findZoneIndex->second));
544998fbe67SJosh Lehan             if (!ptrZoneIndex)
545998fbe67SJosh Lehan             {
546998fbe67SJosh Lehan                 continue;
547998fbe67SJosh Lehan             }
548998fbe67SJosh Lehan 
549998fbe67SJosh Lehan             auto desiredIndex = static_cast<int64_t>(*ptrZoneIndex);
550998fbe67SJosh Lehan             auto grantedIndex = setZoneIndex(name, foundZones, desiredIndex);
551998fbe67SJosh Lehan             std::cout << "Zone " << name << " is at ZoneIndex " << grantedIndex
552998fbe67SJosh Lehan                       << "\n";
553998fbe67SJosh Lehan         }
554998fbe67SJosh Lehan     }
555998fbe67SJosh Lehan 
556998fbe67SJosh Lehan     for (const auto& configuration : configurations)
557998fbe67SJosh Lehan     {
558998fbe67SJosh Lehan         auto findZone =
559998fbe67SJosh Lehan             configuration.second.find(pidZoneConfigurationInterface);
560998fbe67SJosh Lehan         if (findZone != configuration.second.end())
561998fbe67SJosh Lehan         {
562998fbe67SJosh Lehan             const auto& zone = findZone->second;
563998fbe67SJosh Lehan 
564998fbe67SJosh Lehan             const std::string& name = std::get<std::string>(zone.at("Name"));
565998fbe67SJosh Lehan 
566998fbe67SJosh Lehan             auto index = getZoneIndex(name, foundZones);
5678c3c51eeSJames Feist 
568c54fbd88SPatrick Venture             auto& details = zoneDetailsConfig[index];
569998fbe67SJosh Lehan 
5703484bedaSJames Feist             details.minThermalOutput = std::visit(VariantToDoubleVisitor(),
5713484bedaSJames Feist                                                   zone.at("MinThermalOutput"));
5728e2fdb34SPatrick Venture             details.failsafePercent = std::visit(VariantToDoubleVisitor(),
5731f802f5eSJames Feist                                                  zone.at("FailSafePercent"));
5747136a5aeSJames Feist         }
5757136a5aeSJames Feist         auto findBase = configuration.second.find(pidConfigurationInterface);
576f3b04fd3SJason Ling         // loop through all the PID configurations and fill out a sensor config
57722c257abSJames Feist         if (findBase != configuration.second.end())
5787136a5aeSJames Feist         {
57922c257abSJames Feist             const auto& base =
58022c257abSJames Feist                 configuration.second.at(pidConfigurationInterface);
581f3b04fd3SJason Ling             const std::string pidName = std::get<std::string>(base.at("Name"));
582f3b04fd3SJason Ling             const std::string pidClass =
583f3b04fd3SJason Ling                 std::get<std::string>(base.at("Class"));
5848c3c51eeSJames Feist             const std::vector<std::string>& zones =
5851f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
5868c3c51eeSJames Feist             for (const std::string& zone : zones)
5878c3c51eeSJames Feist             {
588998fbe67SJosh Lehan                 auto index = getZoneIndex(zone, foundZones);
589998fbe67SJosh Lehan 
590f81f2886SJames Feist                 conf::PIDConf& conf = zoneConfig[index];
591f3b04fd3SJason Ling                 std::vector<std::string> inputSensorNames(
592f3b04fd3SJason Ling                     std::get<std::vector<std::string>>(base.at("Inputs")));
593f3b04fd3SJason Ling                 std::vector<std::string> outputSensorNames;
59450fdfe39SJames Feist 
595f3b04fd3SJason Ling                 // assumption: all fan pids must have at least one output
596f3b04fd3SJason Ling                 if (pidClass == "fan")
59750fdfe39SJames Feist                 {
598f3b04fd3SJason Ling                     outputSensorNames = std::get<std::vector<std::string>>(
599f3b04fd3SJason Ling                         getPIDAttribute(base, "Outputs"));
60050fdfe39SJames Feist                 }
6011738e2a2SJames Feist 
6028f73ad76SAlex.Song                 bool unavailableAsFailed = true;
6038f73ad76SAlex.Song                 auto findUnavailableAsFailed =
6048f73ad76SAlex.Song                     base.find("InputUnavailableAsFailed");
6058f73ad76SAlex.Song                 if (findUnavailableAsFailed != base.end())
6068f73ad76SAlex.Song                 {
6078f73ad76SAlex.Song                     unavailableAsFailed =
6088f73ad76SAlex.Song                         std::get<bool>(findUnavailableAsFailed->second);
6098f73ad76SAlex.Song                 }
6108f73ad76SAlex.Song 
611f3b04fd3SJason Ling                 std::vector<SensorInterfaceType> inputSensorInterfaces;
612f3b04fd3SJason Ling                 std::vector<SensorInterfaceType> outputSensorInterfaces;
613f3b04fd3SJason Ling                 /* populate an interface list for different sensor direction
614f3b04fd3SJason Ling                  * types (input,output)
615f3b04fd3SJason Ling                  */
616f3b04fd3SJason Ling                 /* take the Inputs from the configuration and generate
617f3b04fd3SJason Ling                  * a list of dbus descriptors (path, interface).
618f3b04fd3SJason Ling                  * Mapping can be many-to-one since an element of Inputs can be
619f3b04fd3SJason Ling                  * a regex
620f3b04fd3SJason Ling                  */
621f3b04fd3SJason Ling                 for (const std::string& sensorName : inputSensorNames)
62250fdfe39SJames Feist                 {
623f3b04fd3SJason Ling                     findSensors(sensors, sensorNameToDbusName(sensorName),
624f3b04fd3SJason Ling                                 inputSensorInterfaces);
625f3b04fd3SJason Ling                 }
626f3b04fd3SJason Ling                 for (const std::string& sensorName : outputSensorNames)
627f3b04fd3SJason Ling                 {
628f3b04fd3SJason Ling                     findSensors(sensors, sensorNameToDbusName(sensorName),
629f3b04fd3SJason Ling                                 outputSensorInterfaces);
63050fdfe39SJames Feist                 }
6311738e2a2SJames Feist 
632f3b04fd3SJason Ling                 inputSensorNames.clear();
633f3b04fd3SJason Ling                 for (const SensorInterfaceType& inputSensorInterface :
634f3b04fd3SJason Ling                      inputSensorInterfaces)
6351738e2a2SJames Feist                 {
636f3b04fd3SJason Ling                     const std::string& dbusInterface =
637f3b04fd3SJason Ling                         inputSensorInterface.second;
638f3b04fd3SJason Ling                     const std::string& inputSensorPath =
639f3b04fd3SJason Ling                         inputSensorInterface.first;
640ed1dafdfSHarvey.Wu                     // todo: maybe un-hardcode this if we run into slower
641ed1dafdfSHarvey.Wu                     // timeouts with sensors
642ed1dafdfSHarvey.Wu                     if (pidClass == "temp")
643ed1dafdfSHarvey.Wu                     {
644f3b04fd3SJason Ling                         std::string inputSensorName =
645f3b04fd3SJason Ling                             getSensorNameFromPath(inputSensorPath);
646f3b04fd3SJason Ling                         auto& config = sensorConfig[inputSensorName];
647f3b04fd3SJason Ling                         inputSensorNames.push_back(inputSensorName);
648f3b04fd3SJason Ling                         config.type = pidClass;
649f3b04fd3SJason Ling                         config.readPath = inputSensorInterface.first;
6502642cb54SJames Feist                         config.timeout = 0;
6513433cb60SJames Feist                         config.ignoreDbusMinMax = true;
6528f73ad76SAlex.Song                         config.unavailableAsFailed = unavailableAsFailed;
65350fdfe39SJames Feist                     }
654f3b04fd3SJason Ling                     if (dbusInterface != sensorInterface)
655f3b04fd3SJason Ling                     {
656f3b04fd3SJason Ling                         /* all expected inputs in the configuration are expected
657f3b04fd3SJason Ling                          * to be sensor interfaces
658f3b04fd3SJason Ling                          */
659f3b04fd3SJason Ling                         throw std::runtime_error(
660f3b04fd3SJason Ling                             "sensor at dbus path [" + inputSensorPath +
661f3b04fd3SJason Ling                             "] has an interface [" + dbusInterface +
662f3b04fd3SJason Ling                             "] that does not match the expected interface of " +
663f3b04fd3SJason Ling                             sensorInterface);
66450fdfe39SJames Feist                     }
66550fdfe39SJames Feist                 }
6661738e2a2SJames Feist 
667f3b04fd3SJason Ling                 /* fan pids need to pair up tach sensors with their pwm
668f3b04fd3SJason Ling                  * counterparts
669f3b04fd3SJason Ling                  */
670f3b04fd3SJason Ling                 if (pidClass == "fan")
671f3b04fd3SJason Ling                 {
672f3b04fd3SJason Ling                     /* If a PID is a fan there should be either
673f3b04fd3SJason Ling                      * (1) one output(pwm) per input(tach)
674f3b04fd3SJason Ling                      * OR
675f3b04fd3SJason Ling                      * (2) one putput(pwm) for all inputs(tach)
676f3b04fd3SJason Ling                      * everything else indicates a bad configuration.
677f3b04fd3SJason Ling                      */
678f3b04fd3SJason Ling                     bool singlePwm = false;
679f3b04fd3SJason Ling                     if (outputSensorInterfaces.size() == 1)
680f3b04fd3SJason Ling                     {
681f3b04fd3SJason Ling                         /* one pwm, set write paths for all fan sensors to it */
682f3b04fd3SJason Ling                         singlePwm = true;
683f3b04fd3SJason Ling                     }
684f3b04fd3SJason Ling                     else if (inputSensorInterfaces.size() ==
685f3b04fd3SJason Ling                              outputSensorInterfaces.size())
686f3b04fd3SJason Ling                     {
687f3b04fd3SJason Ling                         /* one to one mapping, each fan sensor gets its own pwm
688f3b04fd3SJason Ling                          * control */
689f3b04fd3SJason Ling                         singlePwm = false;
690f3b04fd3SJason Ling                     }
691f3b04fd3SJason Ling                     else
692f3b04fd3SJason Ling                     {
693f3b04fd3SJason Ling                         throw std::runtime_error(
694f3b04fd3SJason Ling                             "fan PID has invalid number of Outputs");
695f3b04fd3SJason Ling                     }
696f3b04fd3SJason Ling                     std::string fanSensorName;
697f3b04fd3SJason Ling                     std::string pwmPath;
698f3b04fd3SJason Ling                     std::string pwmInterface;
699ed1dafdfSHarvey.Wu                     std::string pwmSensorName;
700f3b04fd3SJason Ling                     if (singlePwm)
701f3b04fd3SJason Ling                     {
702f3b04fd3SJason Ling                         /* if just a single output(pwm) is provided then use
703f3b04fd3SJason Ling                          * that pwm control path for all the fan sensor write
704f3b04fd3SJason Ling                          * path configs
705f3b04fd3SJason Ling                          */
706f3b04fd3SJason Ling                         pwmPath = outputSensorInterfaces.at(0).first;
707f3b04fd3SJason Ling                         pwmInterface = outputSensorInterfaces.at(0).second;
708f3b04fd3SJason Ling                     }
709f3b04fd3SJason Ling                     for (uint32_t idx = 0; idx < inputSensorInterfaces.size();
710f3b04fd3SJason Ling                          idx++)
711f3b04fd3SJason Ling                     {
712f3b04fd3SJason Ling                         if (!singlePwm)
713f3b04fd3SJason Ling                         {
714f3b04fd3SJason Ling                             pwmPath = outputSensorInterfaces.at(idx).first;
715f3b04fd3SJason Ling                             pwmInterface =
716f3b04fd3SJason Ling                                 outputSensorInterfaces.at(idx).second;
717f3b04fd3SJason Ling                         }
7180911bfe7SPatrick Venture                         if (defaultPwmInterface != pwmInterface)
719f3b04fd3SJason Ling                         {
720f3b04fd3SJason Ling                             throw std::runtime_error(
721f3b04fd3SJason Ling                                 "fan pwm control at dbus path [" + pwmPath +
722f3b04fd3SJason Ling                                 "] has an interface [" + pwmInterface +
723f3b04fd3SJason Ling                                 "] that does not match the expected interface "
724f3b04fd3SJason Ling                                 "of " +
7250911bfe7SPatrick Venture                                 defaultPwmInterface);
726f3b04fd3SJason Ling                         }
727f3b04fd3SJason Ling                         const std::string& fanPath =
728f3b04fd3SJason Ling                             inputSensorInterfaces.at(idx).first;
729f3b04fd3SJason Ling                         fanSensorName = getSensorNameFromPath(fanPath);
730ed1dafdfSHarvey.Wu                         pwmSensorName = getSensorNameFromPath(pwmPath);
731ed1dafdfSHarvey.Wu                         std::string fanPwmIndex = fanSensorName + pwmSensorName;
732ed1dafdfSHarvey.Wu                         inputSensorNames.push_back(fanPwmIndex);
733ed1dafdfSHarvey.Wu                         auto& fanConfig = sensorConfig[fanPwmIndex];
734ed1dafdfSHarvey.Wu                         fanConfig.type = pidClass;
735ed1dafdfSHarvey.Wu                         fanConfig.readPath = fanPath;
736f3b04fd3SJason Ling                         fanConfig.writePath = pwmPath;
73750fdfe39SJames Feist                         // todo: un-hardcode this if there are fans with
73850fdfe39SJames Feist                         // different ranges
739f3b04fd3SJason Ling                         fanConfig.max = 255;
740f3b04fd3SJason Ling                         fanConfig.min = 0;
74150fdfe39SJames Feist                     }
74250fdfe39SJames Feist                 }
74311d243dfSJames Feist                 // if the sensors aren't available in the current state, don't
74411d243dfSJames Feist                 // add them to the configuration.
745f3b04fd3SJason Ling                 if (inputSensorNames.empty())
74611d243dfSJames Feist                 {
74711d243dfSJames Feist                     continue;
74811d243dfSJames Feist                 }
74911d243dfSJames Feist 
7505ec20270SJames Feist                 std::string offsetType;
7515ec20270SJames Feist 
7525ec20270SJames Feist                 // SetPointOffset is a threshold value to pull from the sensor
7535ec20270SJames Feist                 // to apply an offset. For upper thresholds this means the
7545ec20270SJames Feist                 // setpoint is usually negative.
7555ec20270SJames Feist                 auto findSetpointOffset = base.find("SetPointOffset");
7565ec20270SJames Feist                 if (findSetpointOffset != base.end())
7575ec20270SJames Feist                 {
7585ec20270SJames Feist                     offsetType =
7595ec20270SJames Feist                         std::get<std::string>(findSetpointOffset->second);
7605ec20270SJames Feist                     if (std::find(thresholds::types.begin(),
7615ec20270SJames Feist                                   thresholds::types.end(),
7625ec20270SJames Feist                                   offsetType) == thresholds::types.end())
7635ec20270SJames Feist                     {
7645ec20270SJames Feist                         throw std::runtime_error("Unsupported type: " +
7655ec20270SJames Feist                                                  offsetType);
7665ec20270SJames Feist                     }
7675ec20270SJames Feist                 }
7685ec20270SJames Feist 
7695ec20270SJames Feist                 if (offsetType.empty())
7705ec20270SJames Feist                 {
7711df9e879SPatrick Venture                     conf::ControllerInfo& info =
7721f802f5eSJames Feist                         conf[std::get<std::string>(base.at("Name"))];
773f3b04fd3SJason Ling                     info.inputs = std::move(inputSensorNames);
7747382318fSPatrick Venture                     populatePidInfo(bus, base, info, nullptr, sensorConfig);
7757136a5aeSJames Feist                 }
7767136a5aeSJames Feist                 else
7777136a5aeSJames Feist                 {
7785ec20270SJames Feist                     // we have to split up the inputs, as in practice t-control
7795ec20270SJames Feist                     // values will differ, making setpoints differ
780f3b04fd3SJason Ling                     for (const std::string& input : inputSensorNames)
781572c43daSJames Feist                     {
7821df9e879SPatrick Venture                         conf::ControllerInfo& info = conf[input];
7835ec20270SJames Feist                         info.inputs.emplace_back(input);
7847382318fSPatrick Venture                         populatePidInfo(bus, base, info, &offsetType,
7857382318fSPatrick Venture                                         sensorConfig);
786572c43daSJames Feist                     }
787572c43daSJames Feist                 }
7887136a5aeSJames Feist             }
7898c3c51eeSJames Feist         }
79022c257abSJames Feist         auto findStepwise =
79122c257abSJames Feist             configuration.second.find(stepwiseConfigurationInterface);
79222c257abSJames Feist         if (findStepwise != configuration.second.end())
79322c257abSJames Feist         {
79422c257abSJames Feist             const auto& base = findStepwise->second;
79522c257abSJames Feist             const std::vector<std::string>& zones =
7961f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
79722c257abSJames Feist             for (const std::string& zone : zones)
79822c257abSJames Feist             {
799998fbe67SJosh Lehan                 auto index = getZoneIndex(zone, foundZones);
800998fbe67SJosh Lehan 
801f81f2886SJames Feist                 conf::PIDConf& conf = zoneConfig[index];
80250fdfe39SJames Feist 
80350fdfe39SJames Feist                 std::vector<std::string> inputs;
80450fdfe39SJames Feist                 std::vector<std::string> sensorNames =
8051f802f5eSJames Feist                     std::get<std::vector<std::string>>(base.at("Inputs"));
80650fdfe39SJames Feist 
8078f73ad76SAlex.Song                 bool unavailableAsFailed = true;
8088f73ad76SAlex.Song                 auto findUnavailableAsFailed =
8098f73ad76SAlex.Song                     base.find("InputUnavailableAsFailed");
8108f73ad76SAlex.Song                 if (findUnavailableAsFailed != base.end())
8118f73ad76SAlex.Song                 {
8128f73ad76SAlex.Song                     unavailableAsFailed =
8138f73ad76SAlex.Song                         std::get<bool>(findUnavailableAsFailed->second);
8148f73ad76SAlex.Song                 }
8158f73ad76SAlex.Song 
8161738e2a2SJames Feist                 bool sensorFound = false;
81750fdfe39SJames Feist                 for (const std::string& sensorName : sensorNames)
81850fdfe39SJames Feist                 {
8191738e2a2SJames Feist                     std::vector<std::pair<std::string, std::string>>
8201738e2a2SJames Feist                         sensorPathIfacePairs;
821f3b04fd3SJason Ling                     if (!findSensors(sensors, sensorNameToDbusName(sensorName),
822f3b04fd3SJason Ling                                      sensorPathIfacePairs))
82350fdfe39SJames Feist                     {
82450fdfe39SJames Feist                         break;
82550fdfe39SJames Feist                     }
82650fdfe39SJames Feist 
8271738e2a2SJames Feist                     for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
8281738e2a2SJames Feist                     {
8291738e2a2SJames Feist                         size_t idx =
8301738e2a2SJames Feist                             sensorPathIfacePair.first.find_last_of("/") + 1;
8311738e2a2SJames Feist                         std::string shortName =
8321738e2a2SJames Feist                             sensorPathIfacePair.first.substr(idx);
8331738e2a2SJames Feist 
8341738e2a2SJames Feist                         inputs.push_back(shortName);
8351738e2a2SJames Feist                         auto& config = sensorConfig[shortName];
83669c51061SPatrick Venture                         config.readPath = sensorPathIfacePair.first;
83750fdfe39SJames Feist                         config.type = "temp";
8383660b388SJames Feist                         config.ignoreDbusMinMax = true;
8398f73ad76SAlex.Song                         config.unavailableAsFailed = unavailableAsFailed;
84050fdfe39SJames Feist                         // todo: maybe un-hardcode this if we run into slower
84150fdfe39SJames Feist                         // timeouts with sensors
84250fdfe39SJames Feist 
8432642cb54SJames Feist                         config.timeout = 0;
8441738e2a2SJames Feist                         sensorFound = true;
8451738e2a2SJames Feist                     }
84650fdfe39SJames Feist                 }
84750fdfe39SJames Feist                 if (!sensorFound)
84850fdfe39SJames Feist                 {
84950fdfe39SJames Feist                     continue;
85050fdfe39SJames Feist                 }
8511df9e879SPatrick Venture                 conf::ControllerInfo& info =
8521f802f5eSJames Feist                     conf[std::get<std::string>(base.at("Name"))];
85350fdfe39SJames Feist                 info.inputs = std::move(inputs);
85450fdfe39SJames Feist 
85522c257abSJames Feist                 info.type = "stepwise";
85622c257abSJames Feist                 info.stepwiseInfo.ts = 1.0; // currently unused
8573dfaafdaSJames Feist                 info.stepwiseInfo.positiveHysteresis = 0.0;
8583dfaafdaSJames Feist                 info.stepwiseInfo.negativeHysteresis = 0.0;
859608304daSJames Feist                 std::string subtype = std::get<std::string>(base.at("Class"));
860608304daSJames Feist 
861608304daSJames Feist                 info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
8623dfaafdaSJames Feist                 auto findPosHyst = base.find("PositiveHysteresis");
8633dfaafdaSJames Feist                 auto findNegHyst = base.find("NegativeHysteresis");
8643dfaafdaSJames Feist                 if (findPosHyst != base.end())
8653dfaafdaSJames Feist                 {
8661f802f5eSJames Feist                     info.stepwiseInfo.positiveHysteresis = std::visit(
867208abce8SJames Feist                         VariantToDoubleVisitor(), findPosHyst->second);
8683dfaafdaSJames Feist                 }
8693dfaafdaSJames Feist                 if (findNegHyst != base.end())
8703dfaafdaSJames Feist                 {
8715782ab81SJames Feist                     info.stepwiseInfo.negativeHysteresis = std::visit(
872208abce8SJames Feist                         VariantToDoubleVisitor(), findNegHyst->second);
8733dfaafdaSJames Feist                 }
87422c257abSJames Feist                 std::vector<double> readings =
8751f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Reading"));
87622c257abSJames Feist                 if (readings.size() > ec::maxStepwisePoints)
87722c257abSJames Feist                 {
87822c257abSJames Feist                     throw std::invalid_argument("Too many stepwise points.");
87922c257abSJames Feist                 }
88022c257abSJames Feist                 if (readings.empty())
88122c257abSJames Feist                 {
88222c257abSJames Feist                     throw std::invalid_argument(
88322c257abSJames Feist                         "Must have one stepwise point.");
88422c257abSJames Feist                 }
88522c257abSJames Feist                 std::copy(readings.begin(), readings.end(),
88622c257abSJames Feist                           info.stepwiseInfo.reading);
88722c257abSJames Feist                 if (readings.size() < ec::maxStepwisePoints)
88822c257abSJames Feist                 {
88922c257abSJames Feist                     info.stepwiseInfo.reading[readings.size()] =
8905f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
89122c257abSJames Feist                 }
89222c257abSJames Feist                 std::vector<double> outputs =
8931f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Output"));
89422c257abSJames Feist                 if (readings.size() != outputs.size())
89522c257abSJames Feist                 {
89622c257abSJames Feist                     throw std::invalid_argument(
89722c257abSJames Feist                         "Outputs size must match readings");
89822c257abSJames Feist                 }
89922c257abSJames Feist                 std::copy(outputs.begin(), outputs.end(),
90022c257abSJames Feist                           info.stepwiseInfo.output);
90122c257abSJames Feist                 if (outputs.size() < ec::maxStepwisePoints)
90222c257abSJames Feist                 {
90322c257abSJames Feist                     info.stepwiseInfo.output[outputs.size()] =
9045f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
90522c257abSJames Feist                 }
90622c257abSJames Feist             }
90722c257abSJames Feist         }
90822c257abSJames Feist     }
90939199b4dSPatrick Venture     if constexpr (pid_control::conf::DEBUG)
9107136a5aeSJames Feist     {
91139199b4dSPatrick Venture         debugPrint(sensorConfig, zoneConfig, zoneDetailsConfig);
9127136a5aeSJames Feist     }
913c959c429SJames Feist     if (zoneConfig.empty() || zoneDetailsConfig.empty())
91450fdfe39SJames Feist     {
9151fe08952SJames Feist         std::cerr
9161fe08952SJames Feist             << "No fan zones, application pausing until new configuration\n";
9171fe08952SJames Feist         return false;
91850fdfe39SJames Feist     }
9191fe08952SJames Feist     return true;
9207136a5aeSJames Feist }
921a076487aSPatrick Venture 
9227136a5aeSJames Feist } // namespace dbus_configuration
923a076487aSPatrick Venture } // namespace pid_control
924