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
161*10e46efaSJosh Lehan     const std::array<const char*, 2> skipList = {
162*10e46efaSJosh Lehan         "xyz.openbmc_project.Association",
163*10e46efaSJosh Lehan         "xyz.openbmc_project.Association.Definitions"};
164991ebd80SJames Feist 
165b228bc30SPatrick Williams     sdbusplus::message_t message(m);
166991ebd80SJames Feist     if (std::string(message.get_member()) == "InterfacesAdded")
167991ebd80SJames Feist     {
168991ebd80SJames Feist         sdbusplus::message::object_path path;
169991ebd80SJames Feist         std::unordered_map<
170991ebd80SJames Feist             std::string,
171991ebd80SJames Feist             std::unordered_map<std::string, std::variant<Associations, bool>>>
172991ebd80SJames Feist             data;
173991ebd80SJames Feist 
174991ebd80SJames Feist         message.read(path, data);
175991ebd80SJames Feist 
176991ebd80SJames Feist         for (const char* skip : skipList)
177991ebd80SJames Feist         {
178991ebd80SJames Feist             auto find = data.find(skip);
179991ebd80SJames Feist             if (find != data.end())
180991ebd80SJames Feist             {
181991ebd80SJames Feist                 data.erase(find);
182991ebd80SJames Feist                 if (data.empty())
183991ebd80SJames Feist                 {
184991ebd80SJames Feist                     return 1;
185991ebd80SJames Feist                 }
186991ebd80SJames Feist             }
187991ebd80SJames Feist         }
188*10e46efaSJosh Lehan 
189*10e46efaSJosh Lehan         if constexpr (pid_control::conf::DEBUG)
190*10e46efaSJosh Lehan         {
191*10e46efaSJosh Lehan             std::cout << "New config detected: " << path.str << std::endl;
192*10e46efaSJosh Lehan             for (auto& d : data)
193*10e46efaSJosh Lehan             {
194*10e46efaSJosh Lehan                 std::cout << "\tdata is " << d.first << std::endl;
195*10e46efaSJosh Lehan                 for (auto& second : d.second)
196*10e46efaSJosh Lehan                 {
197*10e46efaSJosh Lehan                     std::cout << "\t\tdata is " << second.first << std::endl;
198*10e46efaSJosh Lehan                 }
199*10e46efaSJosh Lehan             }
200*10e46efaSJosh Lehan         }
201991ebd80SJames Feist     }
202991ebd80SJames Feist 
2031fe08952SJames Feist     boost::asio::steady_timer* timer =
2041fe08952SJames Feist         static_cast<boost::asio::steady_timer*>(context);
2051fe08952SJames Feist 
2061fe08952SJames Feist     // do a brief sleep as we tend to get a bunch of these events at
2071fe08952SJames Feist     // once
2081fe08952SJames Feist     timer->expires_after(std::chrono::seconds(2));
2091fe08952SJames Feist     timer->async_wait([](const boost::system::error_code ec) {
2101fe08952SJames Feist         if (ec == boost::asio::error::operation_aborted)
2111fe08952SJames Feist         {
2121fe08952SJames Feist             /* another timer started*/
2131fe08952SJames Feist             return;
2141fe08952SJames Feist         }
2151fe08952SJames Feist 
2161fe08952SJames Feist         std::cout << "New configuration detected, reloading\n.";
217298a95cbSYong Li         tryRestartControlLoops();
2181fe08952SJames Feist     });
2191fe08952SJames Feist 
2201fe08952SJames Feist     return 1;
2211fe08952SJames Feist }
2221fe08952SJames Feist 
223b228bc30SPatrick Williams void createMatches(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer)
2241fe08952SJames Feist {
2251fe08952SJames Feist     // this is a list because the matches can't be moved
226b228bc30SPatrick Williams     static std::list<sdbusplus::bus::match_t> matches;
2271fe08952SJames Feist 
2283987c8b4SJames Feist     const std::array<std::string, 4> interfaces = {
2293987c8b4SJames Feist         thermalControlIface, pidConfigurationInterface,
2303987c8b4SJames Feist         pidZoneConfigurationInterface, stepwiseConfigurationInterface};
2311fe08952SJames Feist 
2321fe08952SJames Feist     // this list only needs to be created once
2331fe08952SJames Feist     if (!matches.empty())
2341fe08952SJames Feist     {
2351fe08952SJames Feist         return;
2361fe08952SJames Feist     }
2371fe08952SJames Feist 
2381fe08952SJames Feist     // we restart when the configuration changes or there are new sensors
2391fe08952SJames Feist     for (const auto& interface : interfaces)
2401fe08952SJames Feist     {
2411fe08952SJames Feist         matches.emplace_back(
2421fe08952SJames Feist             bus,
2431fe08952SJames Feist             "type='signal',member='PropertiesChanged',arg0namespace='" +
2441fe08952SJames Feist                 interface + "'",
2451fe08952SJames Feist             eventHandler, &timer);
2461fe08952SJames Feist     }
2471fe08952SJames Feist     matches.emplace_back(
2481fe08952SJames Feist         bus,
2491fe08952SJames Feist         "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
2501fe08952SJames Feist         "sensors/'",
2511fe08952SJames Feist         eventHandler, &timer);
2521fe08952SJames Feist }
2531fe08952SJames Feist 
2546fc301fbSJason Ling /**
2556fc301fbSJason Ling  * retrieve an attribute from the pid configuration map
2566fc301fbSJason Ling  * @param[in] base - the PID configuration map, keys are the attributes and
2576fc301fbSJason Ling  * value is the variant associated with that attribute.
2586fc301fbSJason Ling  * @param attributeName - the name of the attribute
2596fc301fbSJason Ling  * @return a variant holding the value associated with a key
2606fc301fbSJason Ling  * @throw runtime_error : attributeName is not in base
2616fc301fbSJason Ling  */
2626fc301fbSJason Ling inline DbusVariantType getPIDAttribute(
2636fc301fbSJason Ling     const std::unordered_map<std::string, DbusVariantType>& base,
2646fc301fbSJason Ling     const std::string& attributeName)
2656fc301fbSJason Ling {
2666fc301fbSJason Ling     auto search = base.find(attributeName);
2676fc301fbSJason Ling     if (search == base.end())
2686fc301fbSJason Ling     {
2696fc301fbSJason Ling         throw std::runtime_error("missing attribute " + attributeName);
2706fc301fbSJason Ling     }
2716fc301fbSJason Ling     return search->second;
2726fc301fbSJason Ling }
2736fc301fbSJason Ling 
274239aa7d7SHarvey Wu inline void getCycleTimeSetting(
275239aa7d7SHarvey Wu     const std::unordered_map<std::string, DbusVariantType>& zone,
276239aa7d7SHarvey Wu     const int zoneIndex, const std::string& attributeName, uint64_t& value)
277239aa7d7SHarvey Wu {
278239aa7d7SHarvey Wu     auto findAttributeName = zone.find(attributeName);
279239aa7d7SHarvey Wu     if (findAttributeName != zone.end())
280239aa7d7SHarvey Wu     {
281239aa7d7SHarvey Wu         double tmpAttributeValue =
282239aa7d7SHarvey Wu             std::visit(VariantToDoubleVisitor(), zone.at(attributeName));
283239aa7d7SHarvey Wu         if (tmpAttributeValue >= 1.0)
284239aa7d7SHarvey Wu         {
285239aa7d7SHarvey Wu             value = static_cast<uint64_t>(tmpAttributeValue);
286239aa7d7SHarvey Wu         }
287239aa7d7SHarvey Wu         else
288239aa7d7SHarvey Wu         {
289239aa7d7SHarvey Wu             std::cerr << "Zone " << zoneIndex << ": " << attributeName
290239aa7d7SHarvey Wu                       << " is invalid. Use default " << value << " ms\n";
291239aa7d7SHarvey Wu         }
292239aa7d7SHarvey Wu     }
293239aa7d7SHarvey Wu     else
294239aa7d7SHarvey Wu     {
295239aa7d7SHarvey Wu         std::cerr << "Zone " << zoneIndex << ": " << attributeName
296239aa7d7SHarvey Wu                   << " cannot find setting. Use default " << value << " ms\n";
297239aa7d7SHarvey Wu     }
298239aa7d7SHarvey Wu }
299239aa7d7SHarvey Wu 
3005ec20270SJames Feist void populatePidInfo(
301a1ae4fa1SHarvey.Wu     [[maybe_unused]] sdbusplus::bus_t& bus,
3025ec20270SJames Feist     const std::unordered_map<std::string, DbusVariantType>& base,
3031df9e879SPatrick Venture     conf::ControllerInfo& info, const std::string* thresholdProperty,
3047382318fSPatrick Venture     const std::map<std::string, conf::SensorConfig>& sensorConfig)
3055ec20270SJames Feist {
3066fc301fbSJason Ling     info.type = std::get<std::string>(getPIDAttribute(base, "Class"));
3075ec20270SJames Feist     if (info.type == "fan")
3085ec20270SJames Feist     {
3095ec20270SJames Feist         info.setpoint = 0;
3105ec20270SJames Feist     }
3115ec20270SJames Feist     else
3125ec20270SJames Feist     {
3136fc301fbSJason Ling         info.setpoint = std::visit(VariantToDoubleVisitor(),
3146fc301fbSJason Ling                                    getPIDAttribute(base, "SetPoint"));
3155ec20270SJames Feist     }
3165ec20270SJames Feist 
3175ec20270SJames Feist     if (thresholdProperty != nullptr)
3185ec20270SJames Feist     {
3195ec20270SJames Feist         std::string interface;
3205ec20270SJames Feist         if (*thresholdProperty == "WarningHigh" ||
3215ec20270SJames Feist             *thresholdProperty == "WarningLow")
3225ec20270SJames Feist         {
3235ec20270SJames Feist             interface = thresholds::warningInterface;
3245ec20270SJames Feist         }
3255ec20270SJames Feist         else
3265ec20270SJames Feist         {
3275ec20270SJames Feist             interface = thresholds::criticalInterface;
3285ec20270SJames Feist         }
3297382318fSPatrick Venture         const std::string& path = sensorConfig.at(info.inputs.front()).readPath;
3305ec20270SJames Feist 
3318729eb98SPatrick Venture         DbusHelper helper(sdbusplus::bus::new_system());
3329b93692dSPatrick Venture         std::string service = helper.getService(interface, path);
3335ec20270SJames Feist         double reading = 0;
3345ec20270SJames Feist         try
3355ec20270SJames Feist         {
3369b93692dSPatrick Venture             helper.getProperty(service, path, interface, *thresholdProperty,
3379b93692dSPatrick Venture                                reading);
3385ec20270SJames Feist         }
339b228bc30SPatrick Williams         catch (const sdbusplus::exception_t& ex)
3405ec20270SJames Feist         {
3415ec20270SJames Feist             // unsupported threshold, leaving reading at 0
3425ec20270SJames Feist         }
3435ec20270SJames Feist 
3445ec20270SJames Feist         info.setpoint += reading;
3455ec20270SJames Feist     }
3465ec20270SJames Feist 
3475ec20270SJames Feist     info.pidInfo.ts = 1.0; // currently unused
3486fc301fbSJason Ling     info.pidInfo.proportionalCoeff = std::visit(
3496fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "PCoefficient"));
3506fc301fbSJason Ling     info.pidInfo.integralCoeff = std::visit(
3516fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ICoefficient"));
352c612c051SJosh Lehan     // DCoefficient is below, it is optional, same reason as in buildjson.cpp
3536fc301fbSJason Ling     info.pidInfo.feedFwdOffset = std::visit(
3546fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "FFOffCoefficient"));
3556fc301fbSJason Ling     info.pidInfo.feedFwdGain = std::visit(
3566fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "FFGainCoefficient"));
3576fc301fbSJason Ling     info.pidInfo.integralLimit.max = std::visit(
3586fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMax"));
3596fc301fbSJason Ling     info.pidInfo.integralLimit.min = std::visit(
3606fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMin"));
3616fc301fbSJason Ling     info.pidInfo.outLim.max = std::visit(VariantToDoubleVisitor(),
3626fc301fbSJason Ling                                          getPIDAttribute(base, "OutLimitMax"));
3636fc301fbSJason Ling     info.pidInfo.outLim.min = std::visit(VariantToDoubleVisitor(),
3646fc301fbSJason Ling                                          getPIDAttribute(base, "OutLimitMin"));
3655ec20270SJames Feist     info.pidInfo.slewNeg =
3666fc301fbSJason Ling         std::visit(VariantToDoubleVisitor(), getPIDAttribute(base, "SlewNeg"));
3675ec20270SJames Feist     info.pidInfo.slewPos =
3686fc301fbSJason Ling         std::visit(VariantToDoubleVisitor(), getPIDAttribute(base, "SlewPos"));
369c612c051SJosh Lehan 
3705ec20270SJames Feist     double negativeHysteresis = 0;
3715ec20270SJames Feist     double positiveHysteresis = 0;
372c612c051SJosh Lehan     double derivativeCoeff = 0;
3735ec20270SJames Feist 
3745ec20270SJames Feist     auto findNeg = base.find("NegativeHysteresis");
3755ec20270SJames Feist     auto findPos = base.find("PositiveHysteresis");
376c612c051SJosh Lehan     auto findDerivative = base.find("DCoefficient");
3775ec20270SJames Feist 
3785ec20270SJames Feist     if (findNeg != base.end())
3795ec20270SJames Feist     {
3805ec20270SJames Feist         negativeHysteresis =
3815ec20270SJames Feist             std::visit(VariantToDoubleVisitor(), findNeg->second);
3825ec20270SJames Feist     }
3835ec20270SJames Feist     if (findPos != base.end())
3845ec20270SJames Feist     {
3855ec20270SJames Feist         positiveHysteresis =
3865ec20270SJames Feist             std::visit(VariantToDoubleVisitor(), findPos->second);
3875ec20270SJames Feist     }
388c612c051SJosh Lehan     if (findDerivative != base.end())
389c612c051SJosh Lehan     {
390c612c051SJosh Lehan         derivativeCoeff =
391c612c051SJosh Lehan             std::visit(VariantToDoubleVisitor(), findDerivative->second);
392c612c051SJosh Lehan     }
393c612c051SJosh Lehan 
3945ec20270SJames Feist     info.pidInfo.negativeHysteresis = negativeHysteresis;
3955ec20270SJames Feist     info.pidInfo.positiveHysteresis = positiveHysteresis;
396c612c051SJosh Lehan     info.pidInfo.derivativeCoeff = derivativeCoeff;
3975ec20270SJames Feist }
3985ec20270SJames Feist 
399b228bc30SPatrick Williams bool init(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer,
4007382318fSPatrick Venture           std::map<std::string, conf::SensorConfig>& sensorConfig,
4017382318fSPatrick Venture           std::map<int64_t, conf::PIDConf>& zoneConfig,
4027382318fSPatrick Venture           std::map<int64_t, conf::ZoneConfig>& zoneDetailsConfig)
4031fe08952SJames Feist {
4041fe08952SJames Feist 
4051fe08952SJames Feist     sensorConfig.clear();
4061fe08952SJames Feist     zoneConfig.clear();
4071fe08952SJames Feist     zoneDetailsConfig.clear();
4081fe08952SJames Feist 
4091fe08952SJames Feist     createMatches(bus, timer);
4101fe08952SJames Feist 
4117136a5aeSJames Feist     auto mapper =
4127136a5aeSJames Feist         bus.new_method_call("xyz.openbmc_project.ObjectMapper",
4137136a5aeSJames Feist                             "/xyz/openbmc_project/object_mapper",
4147136a5aeSJames Feist                             "xyz.openbmc_project.ObjectMapper", "GetSubTree");
41526e8c6a9SJames Feist     mapper.append("/", 0,
4160911bfe7SPatrick Venture                   std::array<const char*, 6>{
4170911bfe7SPatrick Venture                       objectManagerInterface, pidConfigurationInterface,
4187136a5aeSJames Feist                       pidZoneConfigurationInterface,
4190911bfe7SPatrick Venture                       stepwiseConfigurationInterface, sensorInterface,
4200911bfe7SPatrick Venture                       defaultPwmInterface});
42122c257abSJames Feist     std::unordered_map<
42222c257abSJames Feist         std::string, std::unordered_map<std::string, std::vector<std::string>>>
42322c257abSJames Feist         respData;
42422c257abSJames Feist     try
42522c257abSJames Feist     {
4267136a5aeSJames Feist         auto resp = bus.call(mapper);
4277136a5aeSJames Feist         resp.read(respData);
42822c257abSJames Feist     }
4290001ee02SPatrick Williams     catch (const sdbusplus::exception_t&)
43022c257abSJames Feist     {
43122c257abSJames Feist         // can't do anything without mapper call data
43222c257abSJames Feist         throw std::runtime_error("ObjectMapper Call Failure");
43322c257abSJames Feist     }
43422c257abSJames Feist 
4357136a5aeSJames Feist     if (respData.empty())
4367136a5aeSJames Feist     {
43722c257abSJames Feist         // can't do anything without mapper call data
4387136a5aeSJames Feist         throw std::runtime_error("No configuration data available from Mapper");
4397136a5aeSJames Feist     }
4407136a5aeSJames Feist     // create a map of pair of <has pid configuration, ObjectManager path>
4417136a5aeSJames Feist     std::unordered_map<std::string, std::pair<bool, std::string>> owners;
4427136a5aeSJames Feist     // and a map of <path, interface> for sensors
4437136a5aeSJames Feist     std::unordered_map<std::string, std::string> sensors;
4447136a5aeSJames Feist     for (const auto& objectPair : respData)
4457136a5aeSJames Feist     {
4467136a5aeSJames Feist         for (const auto& ownerPair : objectPair.second)
4477136a5aeSJames Feist         {
4487136a5aeSJames Feist             auto& owner = owners[ownerPair.first];
4497136a5aeSJames Feist             for (const std::string& interface : ownerPair.second)
4507136a5aeSJames Feist             {
4517136a5aeSJames Feist 
4527136a5aeSJames Feist                 if (interface == objectManagerInterface)
4537136a5aeSJames Feist                 {
4547136a5aeSJames Feist                     owner.second = objectPair.first;
4557136a5aeSJames Feist                 }
4567136a5aeSJames Feist                 if (interface == pidConfigurationInterface ||
45722c257abSJames Feist                     interface == pidZoneConfigurationInterface ||
45822c257abSJames Feist                     interface == stepwiseConfigurationInterface)
4597136a5aeSJames Feist                 {
4607136a5aeSJames Feist                     owner.first = true;
4617136a5aeSJames Feist                 }
4620911bfe7SPatrick Venture                 if (interface == sensorInterface ||
4630911bfe7SPatrick Venture                     interface == defaultPwmInterface)
4647136a5aeSJames Feist                 {
4657136a5aeSJames Feist                     // we're not interested in pwm sensors, just pwm control
4667136a5aeSJames Feist                     if (interface == sensorInterface &&
4677136a5aeSJames Feist                         objectPair.first.find("pwm") != std::string::npos)
4687136a5aeSJames Feist                     {
4697136a5aeSJames Feist                         continue;
4707136a5aeSJames Feist                     }
4717136a5aeSJames Feist                     sensors[objectPair.first] = interface;
4727136a5aeSJames Feist                 }
4737136a5aeSJames Feist             }
4747136a5aeSJames Feist         }
4757136a5aeSJames Feist     }
4767136a5aeSJames Feist     ManagedObjectType configurations;
4777136a5aeSJames Feist     for (const auto& owner : owners)
4787136a5aeSJames Feist     {
4797136a5aeSJames Feist         // skip if no pid configuration (means probably a sensor)
4807136a5aeSJames Feist         if (!owner.second.first)
4817136a5aeSJames Feist         {
4827136a5aeSJames Feist             continue;
4837136a5aeSJames Feist         }
4847136a5aeSJames Feist         auto endpoint = bus.new_method_call(
4857136a5aeSJames Feist             owner.first.c_str(), owner.second.second.c_str(),
4867136a5aeSJames Feist             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
48722c257abSJames Feist         ManagedObjectType configuration;
48822c257abSJames Feist         try
48922c257abSJames Feist         {
4907136a5aeSJames Feist             auto responce = bus.call(endpoint);
4917136a5aeSJames Feist             responce.read(configuration);
49222c257abSJames Feist         }
4930001ee02SPatrick Williams         catch (const sdbusplus::exception_t&)
49422c257abSJames Feist         {
49522c257abSJames Feist             // this shouldn't happen, probably means daemon crashed
49622c257abSJames Feist             throw std::runtime_error("Error getting managed objects from " +
49722c257abSJames Feist                                      owner.first);
49822c257abSJames Feist         }
49922c257abSJames Feist 
5007136a5aeSJames Feist         for (auto& pathPair : configuration)
5017136a5aeSJames Feist         {
5027136a5aeSJames Feist             if (pathPair.second.find(pidConfigurationInterface) !=
5037136a5aeSJames Feist                     pathPair.second.end() ||
5047136a5aeSJames Feist                 pathPair.second.find(pidZoneConfigurationInterface) !=
50522c257abSJames Feist                     pathPair.second.end() ||
50622c257abSJames Feist                 pathPair.second.find(stepwiseConfigurationInterface) !=
5077136a5aeSJames Feist                     pathPair.second.end())
5087136a5aeSJames Feist             {
5097136a5aeSJames Feist                 configurations.emplace(pathPair);
5107136a5aeSJames Feist             }
511f0096a0cSJames Feist         }
512f0096a0cSJames Feist     }
513f0096a0cSJames Feist 
514f0096a0cSJames Feist     // remove controllers from config that aren't in the current profile(s)
515f0096a0cSJames Feist     std::vector<std::string> selectedProfiles = getSelectedProfiles(bus);
516f0096a0cSJames Feist     if (selectedProfiles.size())
517f0096a0cSJames Feist     {
5183987c8b4SJames Feist         for (auto pathIt = configurations.begin();
5193987c8b4SJames Feist              pathIt != configurations.end();)
520f0096a0cSJames Feist         {
5213987c8b4SJames Feist             for (auto confIt = pathIt->second.begin();
5223987c8b4SJames Feist                  confIt != pathIt->second.end();)
5233987c8b4SJames Feist             {
5243987c8b4SJames Feist                 auto profilesFind = confIt->second.find("Profiles");
5253987c8b4SJames Feist                 if (profilesFind == confIt->second.end())
5263987c8b4SJames Feist                 {
5273987c8b4SJames Feist                     confIt++;
5283987c8b4SJames Feist                     continue; // if no profiles selected, apply always
5293987c8b4SJames Feist                 }
5303987c8b4SJames Feist                 auto profiles =
5313987c8b4SJames Feist                     std::get<std::vector<std::string>>(profilesFind->second);
5323987c8b4SJames Feist                 if (profiles.empty())
5333987c8b4SJames Feist                 {
5343987c8b4SJames Feist                     confIt++;
5353987c8b4SJames Feist                     continue;
536f0096a0cSJames Feist                 }
537f0096a0cSJames Feist 
5383987c8b4SJames Feist                 bool found = false;
5393987c8b4SJames Feist                 for (const std::string& profile : profiles)
540f0096a0cSJames Feist                 {
5413987c8b4SJames Feist                     if (std::find(selectedProfiles.begin(),
5423987c8b4SJames Feist                                   selectedProfiles.end(),
5433987c8b4SJames Feist                                   profile) != selectedProfiles.end())
544f0096a0cSJames Feist                     {
5453987c8b4SJames Feist                         found = true;
5463987c8b4SJames Feist                         break;
5473987c8b4SJames Feist                     }
5483987c8b4SJames Feist                 }
5493987c8b4SJames Feist                 if (found)
5503987c8b4SJames Feist                 {
5513987c8b4SJames Feist                     confIt++;
552f0096a0cSJames Feist                 }
553f0096a0cSJames Feist                 else
554f0096a0cSJames Feist                 {
5553987c8b4SJames Feist                     confIt = pathIt->second.erase(confIt);
556f0096a0cSJames Feist                 }
557f0096a0cSJames Feist             }
5583987c8b4SJames Feist             if (pathIt->second.empty())
559f0096a0cSJames Feist             {
5603987c8b4SJames Feist                 pathIt = configurations.erase(pathIt);
561f0096a0cSJames Feist             }
562f0096a0cSJames Feist             else
563f0096a0cSJames Feist             {
5643987c8b4SJames Feist                 pathIt++;
565f0096a0cSJames Feist             }
5667136a5aeSJames Feist         }
5677136a5aeSJames Feist     }
5688c3c51eeSJames Feist 
569998fbe67SJosh Lehan     // On D-Bus, although not necessary,
570998fbe67SJosh Lehan     // having the "zoneID" field can still be useful,
571998fbe67SJosh Lehan     // as it is used for diagnostic messages,
572998fbe67SJosh Lehan     // logging file names, and so on.
573998fbe67SJosh Lehan     // Accept optional "ZoneIndex" parameter to explicitly specify.
574998fbe67SJosh Lehan     // If not present, or not unique, auto-assign index,
575998fbe67SJosh Lehan     // using 0-based numbering, ensuring uniqueness.
576998fbe67SJosh Lehan     std::map<std::string, int64_t> foundZones;
5777136a5aeSJames Feist     for (const auto& configuration : configurations)
5787136a5aeSJames Feist     {
5797136a5aeSJames Feist         auto findZone =
5807136a5aeSJames Feist             configuration.second.find(pidZoneConfigurationInterface);
5817136a5aeSJames Feist         if (findZone != configuration.second.end())
5827136a5aeSJames Feist         {
5837136a5aeSJames Feist             const auto& zone = findZone->second;
584ffd418bbSJames Feist 
5851f802f5eSJames Feist             const std::string& name = std::get<std::string>(zone.at("Name"));
586998fbe67SJosh Lehan 
587998fbe67SJosh Lehan             auto findZoneIndex = zone.find("ZoneIndex");
588998fbe67SJosh Lehan             if (findZoneIndex == zone.end())
589998fbe67SJosh Lehan             {
590998fbe67SJosh Lehan                 continue;
591998fbe67SJosh Lehan             }
592998fbe67SJosh Lehan 
593998fbe67SJosh Lehan             auto ptrZoneIndex = std::get_if<double>(&(findZoneIndex->second));
594998fbe67SJosh Lehan             if (!ptrZoneIndex)
595998fbe67SJosh Lehan             {
596998fbe67SJosh Lehan                 continue;
597998fbe67SJosh Lehan             }
598998fbe67SJosh Lehan 
599998fbe67SJosh Lehan             auto desiredIndex = static_cast<int64_t>(*ptrZoneIndex);
600998fbe67SJosh Lehan             auto grantedIndex = setZoneIndex(name, foundZones, desiredIndex);
601998fbe67SJosh Lehan             std::cout << "Zone " << name << " is at ZoneIndex " << grantedIndex
602998fbe67SJosh Lehan                       << "\n";
603998fbe67SJosh Lehan         }
604998fbe67SJosh Lehan     }
605998fbe67SJosh Lehan 
606998fbe67SJosh Lehan     for (const auto& configuration : configurations)
607998fbe67SJosh Lehan     {
608998fbe67SJosh Lehan         auto findZone =
609998fbe67SJosh Lehan             configuration.second.find(pidZoneConfigurationInterface);
610998fbe67SJosh Lehan         if (findZone != configuration.second.end())
611998fbe67SJosh Lehan         {
612998fbe67SJosh Lehan             const auto& zone = findZone->second;
613998fbe67SJosh Lehan 
614998fbe67SJosh Lehan             const std::string& name = std::get<std::string>(zone.at("Name"));
615998fbe67SJosh Lehan 
616998fbe67SJosh Lehan             auto index = getZoneIndex(name, foundZones);
6178c3c51eeSJames Feist 
618c54fbd88SPatrick Venture             auto& details = zoneDetailsConfig[index];
619998fbe67SJosh Lehan 
6203484bedaSJames Feist             details.minThermalOutput = std::visit(VariantToDoubleVisitor(),
6213484bedaSJames Feist                                                   zone.at("MinThermalOutput"));
6228e2fdb34SPatrick Venture             details.failsafePercent = std::visit(VariantToDoubleVisitor(),
6231f802f5eSJames Feist                                                  zone.at("FailSafePercent"));
6249f9a06aaSJosh Lehan 
625239aa7d7SHarvey Wu             getCycleTimeSetting(zone, index, "CycleIntervalTimeMS",
626239aa7d7SHarvey Wu                                 details.cycleTime.cycleIntervalTimeMS);
627239aa7d7SHarvey Wu             getCycleTimeSetting(zone, index, "UpdateThermalsTimeMS",
628239aa7d7SHarvey Wu                                 details.cycleTime.updateThermalsTimeMS);
6297136a5aeSJames Feist         }
6307136a5aeSJames Feist         auto findBase = configuration.second.find(pidConfigurationInterface);
631f3b04fd3SJason Ling         // loop through all the PID configurations and fill out a sensor config
63222c257abSJames Feist         if (findBase != configuration.second.end())
6337136a5aeSJames Feist         {
63422c257abSJames Feist             const auto& base =
63522c257abSJames Feist                 configuration.second.at(pidConfigurationInterface);
636f3b04fd3SJason Ling             const std::string pidName = std::get<std::string>(base.at("Name"));
637f3b04fd3SJason Ling             const std::string pidClass =
638f3b04fd3SJason Ling                 std::get<std::string>(base.at("Class"));
6398c3c51eeSJames Feist             const std::vector<std::string>& zones =
6401f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
6418c3c51eeSJames Feist             for (const std::string& zone : zones)
6428c3c51eeSJames Feist             {
643998fbe67SJosh Lehan                 auto index = getZoneIndex(zone, foundZones);
644998fbe67SJosh Lehan 
645f81f2886SJames Feist                 conf::PIDConf& conf = zoneConfig[index];
646f3b04fd3SJason Ling                 std::vector<std::string> inputSensorNames(
647f3b04fd3SJason Ling                     std::get<std::vector<std::string>>(base.at("Inputs")));
648f3b04fd3SJason Ling                 std::vector<std::string> outputSensorNames;
64950fdfe39SJames Feist 
650f3b04fd3SJason Ling                 // assumption: all fan pids must have at least one output
651f3b04fd3SJason Ling                 if (pidClass == "fan")
65250fdfe39SJames Feist                 {
653f3b04fd3SJason Ling                     outputSensorNames = std::get<std::vector<std::string>>(
654f3b04fd3SJason Ling                         getPIDAttribute(base, "Outputs"));
65550fdfe39SJames Feist                 }
6561738e2a2SJames Feist 
6578f73ad76SAlex.Song                 bool unavailableAsFailed = true;
6588f73ad76SAlex.Song                 auto findUnavailableAsFailed =
6598f73ad76SAlex.Song                     base.find("InputUnavailableAsFailed");
6608f73ad76SAlex.Song                 if (findUnavailableAsFailed != base.end())
6618f73ad76SAlex.Song                 {
6628f73ad76SAlex.Song                     unavailableAsFailed =
6638f73ad76SAlex.Song                         std::get<bool>(findUnavailableAsFailed->second);
6648f73ad76SAlex.Song                 }
6658f73ad76SAlex.Song 
666f3b04fd3SJason Ling                 std::vector<SensorInterfaceType> inputSensorInterfaces;
667f3b04fd3SJason Ling                 std::vector<SensorInterfaceType> outputSensorInterfaces;
668f3b04fd3SJason Ling                 /* populate an interface list for different sensor direction
669f3b04fd3SJason Ling                  * types (input,output)
670f3b04fd3SJason Ling                  */
671f3b04fd3SJason Ling                 /* take the Inputs from the configuration and generate
672f3b04fd3SJason Ling                  * a list of dbus descriptors (path, interface).
673f3b04fd3SJason Ling                  * Mapping can be many-to-one since an element of Inputs can be
674f3b04fd3SJason Ling                  * a regex
675f3b04fd3SJason Ling                  */
676f3b04fd3SJason Ling                 for (const std::string& sensorName : inputSensorNames)
67750fdfe39SJames Feist                 {
678f3b04fd3SJason Ling                     findSensors(sensors, sensorNameToDbusName(sensorName),
679f3b04fd3SJason Ling                                 inputSensorInterfaces);
680f3b04fd3SJason Ling                 }
681f3b04fd3SJason Ling                 for (const std::string& sensorName : outputSensorNames)
682f3b04fd3SJason Ling                 {
683f3b04fd3SJason Ling                     findSensors(sensors, sensorNameToDbusName(sensorName),
684f3b04fd3SJason Ling                                 outputSensorInterfaces);
68550fdfe39SJames Feist                 }
6861738e2a2SJames Feist 
687f3b04fd3SJason Ling                 inputSensorNames.clear();
688f3b04fd3SJason Ling                 for (const SensorInterfaceType& inputSensorInterface :
689f3b04fd3SJason Ling                      inputSensorInterfaces)
6901738e2a2SJames Feist                 {
691f3b04fd3SJason Ling                     const std::string& dbusInterface =
692f3b04fd3SJason Ling                         inputSensorInterface.second;
693f3b04fd3SJason Ling                     const std::string& inputSensorPath =
694f3b04fd3SJason Ling                         inputSensorInterface.first;
695fb82a87dSJosh Lehan 
696fb82a87dSJosh Lehan                     // Setting timeout to 0 is intentional, as D-Bus passive
697fb82a87dSJosh Lehan                     // sensor updates are pushed in, not pulled by timer poll.
698fb82a87dSJosh Lehan                     // Setting ignoreDbusMinMax is intentional, as this
699fb82a87dSJosh Lehan                     // prevents normalization of values to [0.0, 1.0] range,
700fb82a87dSJosh Lehan                     // which would mess up the PID loop math.
701fb82a87dSJosh Lehan                     // All non-fan PID classes should be initialized this way.
702fb82a87dSJosh Lehan                     // As for why a fan should not use this code path, see
703fb82a87dSJosh Lehan                     // the ed1dafdf168def37c65bfb7a5efd18d9dbe04727 commit.
70423e22b90SJosh Lehan                     if ((pidClass == "temp") || (pidClass == "margin") ||
70523e22b90SJosh Lehan                         (pidClass == "power") || (pidClass == "powersum"))
706ed1dafdfSHarvey.Wu                     {
707f3b04fd3SJason Ling                         std::string inputSensorName =
708f3b04fd3SJason Ling                             getSensorNameFromPath(inputSensorPath);
709f3b04fd3SJason Ling                         auto& config = sensorConfig[inputSensorName];
710f3b04fd3SJason Ling                         inputSensorNames.push_back(inputSensorName);
711f3b04fd3SJason Ling                         config.type = pidClass;
712f3b04fd3SJason Ling                         config.readPath = inputSensorInterface.first;
7132642cb54SJames Feist                         config.timeout = 0;
7143433cb60SJames Feist                         config.ignoreDbusMinMax = true;
7158f73ad76SAlex.Song                         config.unavailableAsFailed = unavailableAsFailed;
71650fdfe39SJames Feist                     }
717fb82a87dSJosh Lehan 
718f3b04fd3SJason Ling                     if (dbusInterface != sensorInterface)
719f3b04fd3SJason Ling                     {
720f3b04fd3SJason Ling                         /* all expected inputs in the configuration are expected
721f3b04fd3SJason Ling                          * to be sensor interfaces
722f3b04fd3SJason Ling                          */
723f3b04fd3SJason Ling                         throw std::runtime_error(
724f3b04fd3SJason Ling                             "sensor at dbus path [" + inputSensorPath +
725f3b04fd3SJason Ling                             "] has an interface [" + dbusInterface +
726f3b04fd3SJason Ling                             "] that does not match the expected interface of " +
727f3b04fd3SJason Ling                             sensorInterface);
72850fdfe39SJames Feist                     }
72950fdfe39SJames Feist                 }
7301738e2a2SJames Feist 
731f3b04fd3SJason Ling                 /* fan pids need to pair up tach sensors with their pwm
732f3b04fd3SJason Ling                  * counterparts
733f3b04fd3SJason Ling                  */
734f3b04fd3SJason Ling                 if (pidClass == "fan")
735f3b04fd3SJason Ling                 {
736f3b04fd3SJason Ling                     /* If a PID is a fan there should be either
737f3b04fd3SJason Ling                      * (1) one output(pwm) per input(tach)
738f3b04fd3SJason Ling                      * OR
739f3b04fd3SJason Ling                      * (2) one putput(pwm) for all inputs(tach)
740f3b04fd3SJason Ling                      * everything else indicates a bad configuration.
741f3b04fd3SJason Ling                      */
742f3b04fd3SJason Ling                     bool singlePwm = false;
743f3b04fd3SJason Ling                     if (outputSensorInterfaces.size() == 1)
744f3b04fd3SJason Ling                     {
745f3b04fd3SJason Ling                         /* one pwm, set write paths for all fan sensors to it */
746f3b04fd3SJason Ling                         singlePwm = true;
747f3b04fd3SJason Ling                     }
748f3b04fd3SJason Ling                     else if (inputSensorInterfaces.size() ==
749f3b04fd3SJason Ling                              outputSensorInterfaces.size())
750f3b04fd3SJason Ling                     {
751f3b04fd3SJason Ling                         /* one to one mapping, each fan sensor gets its own pwm
752f3b04fd3SJason Ling                          * control */
753f3b04fd3SJason Ling                         singlePwm = false;
754f3b04fd3SJason Ling                     }
755f3b04fd3SJason Ling                     else
756f3b04fd3SJason Ling                     {
757f3b04fd3SJason Ling                         throw std::runtime_error(
758f3b04fd3SJason Ling                             "fan PID has invalid number of Outputs");
759f3b04fd3SJason Ling                     }
760f3b04fd3SJason Ling                     std::string fanSensorName;
761f3b04fd3SJason Ling                     std::string pwmPath;
762f3b04fd3SJason Ling                     std::string pwmInterface;
763ed1dafdfSHarvey.Wu                     std::string pwmSensorName;
764f3b04fd3SJason Ling                     if (singlePwm)
765f3b04fd3SJason Ling                     {
766f3b04fd3SJason Ling                         /* if just a single output(pwm) is provided then use
767f3b04fd3SJason Ling                          * that pwm control path for all the fan sensor write
768f3b04fd3SJason Ling                          * path configs
769f3b04fd3SJason Ling                          */
770f3b04fd3SJason Ling                         pwmPath = outputSensorInterfaces.at(0).first;
771f3b04fd3SJason Ling                         pwmInterface = outputSensorInterfaces.at(0).second;
772f3b04fd3SJason Ling                     }
773f3b04fd3SJason Ling                     for (uint32_t idx = 0; idx < inputSensorInterfaces.size();
774f3b04fd3SJason Ling                          idx++)
775f3b04fd3SJason Ling                     {
776f3b04fd3SJason Ling                         if (!singlePwm)
777f3b04fd3SJason Ling                         {
778f3b04fd3SJason Ling                             pwmPath = outputSensorInterfaces.at(idx).first;
779f3b04fd3SJason Ling                             pwmInterface =
780f3b04fd3SJason Ling                                 outputSensorInterfaces.at(idx).second;
781f3b04fd3SJason Ling                         }
7820911bfe7SPatrick Venture                         if (defaultPwmInterface != pwmInterface)
783f3b04fd3SJason Ling                         {
784f3b04fd3SJason Ling                             throw std::runtime_error(
785f3b04fd3SJason Ling                                 "fan pwm control at dbus path [" + pwmPath +
786f3b04fd3SJason Ling                                 "] has an interface [" + pwmInterface +
787f3b04fd3SJason Ling                                 "] that does not match the expected interface "
788f3b04fd3SJason Ling                                 "of " +
7890911bfe7SPatrick Venture                                 defaultPwmInterface);
790f3b04fd3SJason Ling                         }
791f3b04fd3SJason Ling                         const std::string& fanPath =
792f3b04fd3SJason Ling                             inputSensorInterfaces.at(idx).first;
793f3b04fd3SJason Ling                         fanSensorName = getSensorNameFromPath(fanPath);
794ed1dafdfSHarvey.Wu                         pwmSensorName = getSensorNameFromPath(pwmPath);
795ed1dafdfSHarvey.Wu                         std::string fanPwmIndex = fanSensorName + pwmSensorName;
796ed1dafdfSHarvey.Wu                         inputSensorNames.push_back(fanPwmIndex);
797ed1dafdfSHarvey.Wu                         auto& fanConfig = sensorConfig[fanPwmIndex];
798ed1dafdfSHarvey.Wu                         fanConfig.type = pidClass;
799ed1dafdfSHarvey.Wu                         fanConfig.readPath = fanPath;
800f3b04fd3SJason Ling                         fanConfig.writePath = pwmPath;
80150fdfe39SJames Feist                         // todo: un-hardcode this if there are fans with
80250fdfe39SJames Feist                         // different ranges
803f3b04fd3SJason Ling                         fanConfig.max = 255;
804f3b04fd3SJason Ling                         fanConfig.min = 0;
80550fdfe39SJames Feist                     }
80650fdfe39SJames Feist                 }
80711d243dfSJames Feist                 // if the sensors aren't available in the current state, don't
80811d243dfSJames Feist                 // add them to the configuration.
809f3b04fd3SJason Ling                 if (inputSensorNames.empty())
81011d243dfSJames Feist                 {
81111d243dfSJames Feist                     continue;
81211d243dfSJames Feist                 }
81311d243dfSJames Feist 
8145ec20270SJames Feist                 std::string offsetType;
8155ec20270SJames Feist 
8165ec20270SJames Feist                 // SetPointOffset is a threshold value to pull from the sensor
8175ec20270SJames Feist                 // to apply an offset. For upper thresholds this means the
8185ec20270SJames Feist                 // setpoint is usually negative.
8195ec20270SJames Feist                 auto findSetpointOffset = base.find("SetPointOffset");
8205ec20270SJames Feist                 if (findSetpointOffset != base.end())
8215ec20270SJames Feist                 {
8225ec20270SJames Feist                     offsetType =
8235ec20270SJames Feist                         std::get<std::string>(findSetpointOffset->second);
8245ec20270SJames Feist                     if (std::find(thresholds::types.begin(),
8255ec20270SJames Feist                                   thresholds::types.end(),
8265ec20270SJames Feist                                   offsetType) == thresholds::types.end())
8275ec20270SJames Feist                     {
8285ec20270SJames Feist                         throw std::runtime_error("Unsupported type: " +
8295ec20270SJames Feist                                                  offsetType);
8305ec20270SJames Feist                     }
8315ec20270SJames Feist                 }
8325ec20270SJames Feist 
8335ec20270SJames Feist                 if (offsetType.empty())
8345ec20270SJames Feist                 {
8351df9e879SPatrick Venture                     conf::ControllerInfo& info =
8361f802f5eSJames Feist                         conf[std::get<std::string>(base.at("Name"))];
837f3b04fd3SJason Ling                     info.inputs = std::move(inputSensorNames);
8387382318fSPatrick Venture                     populatePidInfo(bus, base, info, nullptr, sensorConfig);
8397136a5aeSJames Feist                 }
8407136a5aeSJames Feist                 else
8417136a5aeSJames Feist                 {
8425ec20270SJames Feist                     // we have to split up the inputs, as in practice t-control
8435ec20270SJames Feist                     // values will differ, making setpoints differ
844f3b04fd3SJason Ling                     for (const std::string& input : inputSensorNames)
845572c43daSJames Feist                     {
8461df9e879SPatrick Venture                         conf::ControllerInfo& info = conf[input];
8475ec20270SJames Feist                         info.inputs.emplace_back(input);
8487382318fSPatrick Venture                         populatePidInfo(bus, base, info, &offsetType,
8497382318fSPatrick Venture                                         sensorConfig);
850572c43daSJames Feist                     }
851572c43daSJames Feist                 }
8527136a5aeSJames Feist             }
8538c3c51eeSJames Feist         }
85422c257abSJames Feist         auto findStepwise =
85522c257abSJames Feist             configuration.second.find(stepwiseConfigurationInterface);
85622c257abSJames Feist         if (findStepwise != configuration.second.end())
85722c257abSJames Feist         {
85822c257abSJames Feist             const auto& base = findStepwise->second;
85922c257abSJames Feist             const std::vector<std::string>& zones =
8601f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
86122c257abSJames Feist             for (const std::string& zone : zones)
86222c257abSJames Feist             {
863998fbe67SJosh Lehan                 auto index = getZoneIndex(zone, foundZones);
864998fbe67SJosh Lehan 
865f81f2886SJames Feist                 conf::PIDConf& conf = zoneConfig[index];
86650fdfe39SJames Feist 
86750fdfe39SJames Feist                 std::vector<std::string> inputs;
86850fdfe39SJames Feist                 std::vector<std::string> sensorNames =
8691f802f5eSJames Feist                     std::get<std::vector<std::string>>(base.at("Inputs"));
87050fdfe39SJames Feist 
8718f73ad76SAlex.Song                 bool unavailableAsFailed = true;
8728f73ad76SAlex.Song                 auto findUnavailableAsFailed =
8738f73ad76SAlex.Song                     base.find("InputUnavailableAsFailed");
8748f73ad76SAlex.Song                 if (findUnavailableAsFailed != base.end())
8758f73ad76SAlex.Song                 {
8768f73ad76SAlex.Song                     unavailableAsFailed =
8778f73ad76SAlex.Song                         std::get<bool>(findUnavailableAsFailed->second);
8788f73ad76SAlex.Song                 }
8798f73ad76SAlex.Song 
8801738e2a2SJames Feist                 bool sensorFound = false;
88150fdfe39SJames Feist                 for (const std::string& sensorName : sensorNames)
88250fdfe39SJames Feist                 {
8831738e2a2SJames Feist                     std::vector<std::pair<std::string, std::string>>
8841738e2a2SJames Feist                         sensorPathIfacePairs;
885f3b04fd3SJason Ling                     if (!findSensors(sensors, sensorNameToDbusName(sensorName),
886f3b04fd3SJason Ling                                      sensorPathIfacePairs))
88750fdfe39SJames Feist                     {
88850fdfe39SJames Feist                         break;
88950fdfe39SJames Feist                     }
89050fdfe39SJames Feist 
8911738e2a2SJames Feist                     for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
8921738e2a2SJames Feist                     {
8931738e2a2SJames Feist                         size_t idx =
8941738e2a2SJames Feist                             sensorPathIfacePair.first.find_last_of("/") + 1;
8951738e2a2SJames Feist                         std::string shortName =
8961738e2a2SJames Feist                             sensorPathIfacePair.first.substr(idx);
8971738e2a2SJames Feist 
8981738e2a2SJames Feist                         inputs.push_back(shortName);
8991738e2a2SJames Feist                         auto& config = sensorConfig[shortName];
90069c51061SPatrick Venture                         config.readPath = sensorPathIfacePair.first;
90150fdfe39SJames Feist                         config.type = "temp";
9023660b388SJames Feist                         config.ignoreDbusMinMax = true;
9038f73ad76SAlex.Song                         config.unavailableAsFailed = unavailableAsFailed;
90450fdfe39SJames Feist                         // todo: maybe un-hardcode this if we run into slower
90550fdfe39SJames Feist                         // timeouts with sensors
90650fdfe39SJames Feist 
9072642cb54SJames Feist                         config.timeout = 0;
9081738e2a2SJames Feist                         sensorFound = true;
9091738e2a2SJames Feist                     }
91050fdfe39SJames Feist                 }
91150fdfe39SJames Feist                 if (!sensorFound)
91250fdfe39SJames Feist                 {
91350fdfe39SJames Feist                     continue;
91450fdfe39SJames Feist                 }
9151df9e879SPatrick Venture                 conf::ControllerInfo& info =
9161f802f5eSJames Feist                     conf[std::get<std::string>(base.at("Name"))];
91750fdfe39SJames Feist                 info.inputs = std::move(inputs);
91850fdfe39SJames Feist 
91922c257abSJames Feist                 info.type = "stepwise";
92022c257abSJames Feist                 info.stepwiseInfo.ts = 1.0; // currently unused
9213dfaafdaSJames Feist                 info.stepwiseInfo.positiveHysteresis = 0.0;
9223dfaafdaSJames Feist                 info.stepwiseInfo.negativeHysteresis = 0.0;
923608304daSJames Feist                 std::string subtype = std::get<std::string>(base.at("Class"));
924608304daSJames Feist 
925608304daSJames Feist                 info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
9263dfaafdaSJames Feist                 auto findPosHyst = base.find("PositiveHysteresis");
9273dfaafdaSJames Feist                 auto findNegHyst = base.find("NegativeHysteresis");
9283dfaafdaSJames Feist                 if (findPosHyst != base.end())
9293dfaafdaSJames Feist                 {
9301f802f5eSJames Feist                     info.stepwiseInfo.positiveHysteresis = std::visit(
931208abce8SJames Feist                         VariantToDoubleVisitor(), findPosHyst->second);
9323dfaafdaSJames Feist                 }
9333dfaafdaSJames Feist                 if (findNegHyst != base.end())
9343dfaafdaSJames Feist                 {
9355782ab81SJames Feist                     info.stepwiseInfo.negativeHysteresis = std::visit(
936208abce8SJames Feist                         VariantToDoubleVisitor(), findNegHyst->second);
9373dfaafdaSJames Feist                 }
93822c257abSJames Feist                 std::vector<double> readings =
9391f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Reading"));
94022c257abSJames Feist                 if (readings.size() > ec::maxStepwisePoints)
94122c257abSJames Feist                 {
94222c257abSJames Feist                     throw std::invalid_argument("Too many stepwise points.");
94322c257abSJames Feist                 }
94422c257abSJames Feist                 if (readings.empty())
94522c257abSJames Feist                 {
94622c257abSJames Feist                     throw std::invalid_argument(
94722c257abSJames Feist                         "Must have one stepwise point.");
94822c257abSJames Feist                 }
94922c257abSJames Feist                 std::copy(readings.begin(), readings.end(),
95022c257abSJames Feist                           info.stepwiseInfo.reading);
95122c257abSJames Feist                 if (readings.size() < ec::maxStepwisePoints)
95222c257abSJames Feist                 {
95322c257abSJames Feist                     info.stepwiseInfo.reading[readings.size()] =
9545f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
95522c257abSJames Feist                 }
95622c257abSJames Feist                 std::vector<double> outputs =
9571f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Output"));
95822c257abSJames Feist                 if (readings.size() != outputs.size())
95922c257abSJames Feist                 {
96022c257abSJames Feist                     throw std::invalid_argument(
96122c257abSJames Feist                         "Outputs size must match readings");
96222c257abSJames Feist                 }
96322c257abSJames Feist                 std::copy(outputs.begin(), outputs.end(),
96422c257abSJames Feist                           info.stepwiseInfo.output);
96522c257abSJames Feist                 if (outputs.size() < ec::maxStepwisePoints)
96622c257abSJames Feist                 {
96722c257abSJames Feist                     info.stepwiseInfo.output[outputs.size()] =
9685f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
96922c257abSJames Feist                 }
97022c257abSJames Feist             }
97122c257abSJames Feist         }
97222c257abSJames Feist     }
97339199b4dSPatrick Venture     if constexpr (pid_control::conf::DEBUG)
9747136a5aeSJames Feist     {
97539199b4dSPatrick Venture         debugPrint(sensorConfig, zoneConfig, zoneDetailsConfig);
9767136a5aeSJames Feist     }
977c959c429SJames Feist     if (zoneConfig.empty() || zoneDetailsConfig.empty())
97850fdfe39SJames Feist     {
9791fe08952SJames Feist         std::cerr
9801fe08952SJames Feist             << "No fan zones, application pausing until new configuration\n";
9811fe08952SJames Feist         return false;
98250fdfe39SJames Feist     }
9831fe08952SJames Feist     return true;
9847136a5aeSJames Feist }
985a076487aSPatrick Venture 
9867136a5aeSJames Feist } // namespace dbus_configuration
987a076487aSPatrick Venture } // namespace pid_control
988