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;
878c051121SPatrick Williams     auto mapper = bus.new_method_call("xyz.openbmc_project.ObjectMapper",
88f0096a0cSJames Feist                                       "/xyz/openbmc_project/object_mapper",
898c051121SPatrick Williams                                       "xyz.openbmc_project.ObjectMapper",
908c051121SPatrick Williams                                       "GetSubTree");
91f0096a0cSJames Feist     mapper.append("/", 0, std::array<const char*, 1>{thermalControlIface});
92f0096a0cSJames Feist     std::unordered_map<
93f0096a0cSJames Feist         std::string, std::unordered_map<std::string, std::vector<std::string>>>
94f0096a0cSJames Feist         respData;
95f0096a0cSJames Feist 
96f0096a0cSJames Feist     try
97f0096a0cSJames Feist     {
98f0096a0cSJames Feist         auto resp = bus.call(mapper);
99f0096a0cSJames Feist         resp.read(respData);
100f0096a0cSJames Feist     }
1010001ee02SPatrick Williams     catch (const sdbusplus::exception_t&)
102f0096a0cSJames Feist     {
103f0096a0cSJames Feist         // can't do anything without mapper call data
104f0096a0cSJames Feist         throw std::runtime_error("ObjectMapper Call Failure");
105f0096a0cSJames Feist     }
106f0096a0cSJames Feist     if (respData.empty())
107f0096a0cSJames Feist     {
108f0096a0cSJames Feist         // if the user has profiles but doesn't expose the interface to select
109f0096a0cSJames Feist         // one, just go ahead without using profiles
110f0096a0cSJames Feist         return ret;
111f0096a0cSJames Feist     }
112f0096a0cSJames Feist 
113f0096a0cSJames Feist     // assumption is that we should only have a small handful of selected
114f0096a0cSJames Feist     // profiles at a time (probably only 1), so calling each individually should
115f0096a0cSJames Feist     // not incur a large cost
116f0096a0cSJames Feist     for (const auto& objectPair : respData)
117f0096a0cSJames Feist     {
118f0096a0cSJames Feist         const std::string& path = objectPair.first;
119f0096a0cSJames Feist         for (const auto& ownerPair : objectPair.second)
120f0096a0cSJames Feist         {
121f0096a0cSJames Feist             const std::string& busName = ownerPair.first;
122f0096a0cSJames Feist             auto getProfile =
123f0096a0cSJames Feist                 bus.new_method_call(busName.c_str(), path.c_str(),
124f0096a0cSJames Feist                                     "org.freedesktop.DBus.Properties", "Get");
125f0096a0cSJames Feist             getProfile.append(thermalControlIface, "Current");
126f0096a0cSJames Feist             std::variant<std::string> variantResp;
127f0096a0cSJames Feist             try
128f0096a0cSJames Feist             {
129f0096a0cSJames Feist                 auto resp = bus.call(getProfile);
130f0096a0cSJames Feist                 resp.read(variantResp);
131f0096a0cSJames Feist             }
1320001ee02SPatrick Williams             catch (const sdbusplus::exception_t&)
133f0096a0cSJames Feist             {
134f0096a0cSJames Feist                 throw std::runtime_error("Failure getting profile");
135f0096a0cSJames Feist             }
136f0096a0cSJames Feist             std::string mode = std::get<std::string>(variantResp);
137f0096a0cSJames Feist             ret.emplace_back(std::move(mode));
138f0096a0cSJames Feist         }
139f0096a0cSJames Feist     }
14039199b4dSPatrick Venture     if constexpr (pid_control::conf::DEBUG)
141f0096a0cSJames Feist     {
142f0096a0cSJames Feist         std::cout << "Profiles selected: ";
143f0096a0cSJames Feist         for (const auto& profile : ret)
144f0096a0cSJames Feist         {
145f0096a0cSJames Feist             std::cout << profile << " ";
146f0096a0cSJames Feist         }
147f0096a0cSJames Feist         std::cout << "\n";
148f0096a0cSJames Feist     }
149f0096a0cSJames Feist     return ret;
150f0096a0cSJames Feist }
151f0096a0cSJames Feist 
152991ebd80SJames Feist int eventHandler(sd_bus_message* m, void* context, sd_bus_error*)
1537136a5aeSJames Feist {
154991ebd80SJames Feist     if (context == nullptr || m == nullptr)
1551fe08952SJames Feist     {
1561fe08952SJames Feist         throw std::runtime_error("Invalid match");
1571fe08952SJames Feist     }
158991ebd80SJames Feist 
159991ebd80SJames Feist     // we skip associations because the mapper populates these, not the sensors
16010e46efaSJosh Lehan     const std::array<const char*, 2> skipList = {
16110e46efaSJosh Lehan         "xyz.openbmc_project.Association",
16210e46efaSJosh Lehan         "xyz.openbmc_project.Association.Definitions"};
163991ebd80SJames Feist 
164b228bc30SPatrick Williams     sdbusplus::message_t message(m);
165991ebd80SJames Feist     if (std::string(message.get_member()) == "InterfacesAdded")
166991ebd80SJames Feist     {
167991ebd80SJames Feist         sdbusplus::message::object_path path;
168991ebd80SJames Feist         std::unordered_map<
169991ebd80SJames Feist             std::string,
170991ebd80SJames Feist             std::unordered_map<std::string, std::variant<Associations, bool>>>
171991ebd80SJames Feist             data;
172991ebd80SJames Feist 
173991ebd80SJames Feist         message.read(path, data);
174991ebd80SJames Feist 
175991ebd80SJames Feist         for (const char* skip : skipList)
176991ebd80SJames Feist         {
177991ebd80SJames Feist             auto find = data.find(skip);
178991ebd80SJames Feist             if (find != data.end())
179991ebd80SJames Feist             {
180991ebd80SJames Feist                 data.erase(find);
181991ebd80SJames Feist                 if (data.empty())
182991ebd80SJames Feist                 {
183991ebd80SJames Feist                     return 1;
184991ebd80SJames Feist                 }
185991ebd80SJames Feist             }
186991ebd80SJames Feist         }
18710e46efaSJosh Lehan 
18810e46efaSJosh Lehan         if constexpr (pid_control::conf::DEBUG)
18910e46efaSJosh Lehan         {
19010e46efaSJosh Lehan             std::cout << "New config detected: " << path.str << std::endl;
19110e46efaSJosh Lehan             for (auto& d : data)
19210e46efaSJosh Lehan             {
19310e46efaSJosh Lehan                 std::cout << "\tdata is " << d.first << std::endl;
19410e46efaSJosh Lehan                 for (auto& second : d.second)
19510e46efaSJosh Lehan                 {
19610e46efaSJosh Lehan                     std::cout << "\t\tdata is " << second.first << std::endl;
19710e46efaSJosh Lehan                 }
19810e46efaSJosh Lehan             }
19910e46efaSJosh Lehan         }
200991ebd80SJames Feist     }
201991ebd80SJames Feist 
2021fe08952SJames Feist     boost::asio::steady_timer* timer =
2031fe08952SJames Feist         static_cast<boost::asio::steady_timer*>(context);
2041fe08952SJames Feist 
2051fe08952SJames Feist     // do a brief sleep as we tend to get a bunch of these events at
2061fe08952SJames Feist     // once
2071fe08952SJames Feist     timer->expires_after(std::chrono::seconds(2));
2081fe08952SJames Feist     timer->async_wait([](const boost::system::error_code ec) {
2091fe08952SJames Feist         if (ec == boost::asio::error::operation_aborted)
2101fe08952SJames Feist         {
2111fe08952SJames Feist             /* another timer started*/
2121fe08952SJames Feist             return;
2131fe08952SJames Feist         }
2141fe08952SJames Feist 
2151fe08952SJames Feist         std::cout << "New configuration detected, reloading\n.";
216298a95cbSYong Li         tryRestartControlLoops();
2171fe08952SJames Feist     });
2181fe08952SJames Feist 
2191fe08952SJames Feist     return 1;
2201fe08952SJames Feist }
2211fe08952SJames Feist 
222b228bc30SPatrick Williams void createMatches(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer)
2231fe08952SJames Feist {
2241fe08952SJames Feist     // this is a list because the matches can't be moved
225b228bc30SPatrick Williams     static std::list<sdbusplus::bus::match_t> matches;
2261fe08952SJames Feist 
2273987c8b4SJames Feist     const std::array<std::string, 4> interfaces = {
2283987c8b4SJames Feist         thermalControlIface, pidConfigurationInterface,
2293987c8b4SJames Feist         pidZoneConfigurationInterface, stepwiseConfigurationInterface};
2301fe08952SJames Feist 
2311fe08952SJames Feist     // this list only needs to be created once
2321fe08952SJames Feist     if (!matches.empty())
2331fe08952SJames Feist     {
2341fe08952SJames Feist         return;
2351fe08952SJames Feist     }
2361fe08952SJames Feist 
2371fe08952SJames Feist     // we restart when the configuration changes or there are new sensors
2381fe08952SJames Feist     for (const auto& interface : interfaces)
2391fe08952SJames Feist     {
2401fe08952SJames Feist         matches.emplace_back(
2411fe08952SJames Feist             bus,
2421fe08952SJames Feist             "type='signal',member='PropertiesChanged',arg0namespace='" +
2431fe08952SJames Feist                 interface + "'",
2441fe08952SJames Feist             eventHandler, &timer);
2451fe08952SJames Feist     }
2461fe08952SJames Feist     matches.emplace_back(
2471fe08952SJames Feist         bus,
2481fe08952SJames Feist         "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
2491fe08952SJames Feist         "sensors/'",
2501fe08952SJames Feist         eventHandler, &timer);
251c2a311b0SJinliang Wang     matches.emplace_back(bus,
252c2a311b0SJinliang Wang                          "type='signal',member='InterfacesRemoved',arg0path='/"
253c2a311b0SJinliang Wang                          "xyz/openbmc_project/sensors/'",
254c2a311b0SJinliang Wang                          eventHandler, &timer);
2551fe08952SJames Feist }
2561fe08952SJames Feist 
2576fc301fbSJason Ling /**
2586fc301fbSJason Ling  * retrieve an attribute from the pid configuration map
2596fc301fbSJason Ling  * @param[in] base - the PID configuration map, keys are the attributes and
2606fc301fbSJason Ling  * value is the variant associated with that attribute.
2616fc301fbSJason Ling  * @param attributeName - the name of the attribute
2626fc301fbSJason Ling  * @return a variant holding the value associated with a key
2636fc301fbSJason Ling  * @throw runtime_error : attributeName is not in base
2646fc301fbSJason Ling  */
2656fc301fbSJason Ling inline DbusVariantType getPIDAttribute(
2666fc301fbSJason Ling     const std::unordered_map<std::string, DbusVariantType>& base,
2676fc301fbSJason Ling     const std::string& attributeName)
2686fc301fbSJason Ling {
2696fc301fbSJason Ling     auto search = base.find(attributeName);
2706fc301fbSJason Ling     if (search == base.end())
2716fc301fbSJason Ling     {
2726fc301fbSJason Ling         throw std::runtime_error("missing attribute " + attributeName);
2736fc301fbSJason Ling     }
2746fc301fbSJason Ling     return search->second;
2756fc301fbSJason Ling }
2766fc301fbSJason Ling 
277239aa7d7SHarvey Wu inline void getCycleTimeSetting(
278239aa7d7SHarvey Wu     const std::unordered_map<std::string, DbusVariantType>& zone,
279239aa7d7SHarvey Wu     const int zoneIndex, const std::string& attributeName, uint64_t& value)
280239aa7d7SHarvey Wu {
281239aa7d7SHarvey Wu     auto findAttributeName = zone.find(attributeName);
282239aa7d7SHarvey Wu     if (findAttributeName != zone.end())
283239aa7d7SHarvey Wu     {
2848c051121SPatrick Williams         double tmpAttributeValue = std::visit(VariantToDoubleVisitor(),
2858c051121SPatrick Williams                                               zone.at(attributeName));
286239aa7d7SHarvey Wu         if (tmpAttributeValue >= 1.0)
287239aa7d7SHarvey Wu         {
288239aa7d7SHarvey Wu             value = static_cast<uint64_t>(tmpAttributeValue);
289239aa7d7SHarvey Wu         }
290239aa7d7SHarvey Wu         else
291239aa7d7SHarvey Wu         {
292239aa7d7SHarvey Wu             std::cerr << "Zone " << zoneIndex << ": " << attributeName
293239aa7d7SHarvey Wu                       << " is invalid. Use default " << value << " ms\n";
294239aa7d7SHarvey Wu         }
295239aa7d7SHarvey Wu     }
296239aa7d7SHarvey Wu     else
297239aa7d7SHarvey Wu     {
298239aa7d7SHarvey Wu         std::cerr << "Zone " << zoneIndex << ": " << attributeName
299239aa7d7SHarvey Wu                   << " cannot find setting. Use default " << value << " ms\n";
300239aa7d7SHarvey Wu     }
301239aa7d7SHarvey Wu }
302239aa7d7SHarvey Wu 
3035ec20270SJames Feist void populatePidInfo(
304a1ae4fa1SHarvey.Wu     [[maybe_unused]] sdbusplus::bus_t& bus,
3055ec20270SJames Feist     const std::unordered_map<std::string, DbusVariantType>& base,
3061df9e879SPatrick Venture     conf::ControllerInfo& info, const std::string* thresholdProperty,
3077382318fSPatrick Venture     const std::map<std::string, conf::SensorConfig>& sensorConfig)
3085ec20270SJames Feist {
3096fc301fbSJason Ling     info.type = std::get<std::string>(getPIDAttribute(base, "Class"));
3105ec20270SJames Feist     if (info.type == "fan")
3115ec20270SJames Feist     {
3125ec20270SJames Feist         info.setpoint = 0;
3135ec20270SJames Feist     }
3145ec20270SJames Feist     else
3155ec20270SJames Feist     {
3166fc301fbSJason Ling         info.setpoint = std::visit(VariantToDoubleVisitor(),
3176fc301fbSJason Ling                                    getPIDAttribute(base, "SetPoint"));
3185ec20270SJames Feist     }
3195ec20270SJames Feist 
3209fe3a3c7Sykchiu     int failsafepercent = 0;
3219fe3a3c7Sykchiu     auto findFailSafe = base.find("FailSafePercent");
3229fe3a3c7Sykchiu     if (findFailSafe != base.end())
3239fe3a3c7Sykchiu     {
3249fe3a3c7Sykchiu         failsafepercent = std::visit(VariantToDoubleVisitor(),
3259fe3a3c7Sykchiu                                      getPIDAttribute(base, "FailSafePercent"));
3269fe3a3c7Sykchiu     }
3279fe3a3c7Sykchiu     info.failSafePercent = failsafepercent;
3289fe3a3c7Sykchiu 
3295ec20270SJames Feist     if (thresholdProperty != nullptr)
3305ec20270SJames Feist     {
3315ec20270SJames Feist         std::string interface;
3325ec20270SJames Feist         if (*thresholdProperty == "WarningHigh" ||
3335ec20270SJames Feist             *thresholdProperty == "WarningLow")
3345ec20270SJames Feist         {
3355ec20270SJames Feist             interface = thresholds::warningInterface;
3365ec20270SJames Feist         }
3375ec20270SJames Feist         else
3385ec20270SJames Feist         {
3395ec20270SJames Feist             interface = thresholds::criticalInterface;
3405ec20270SJames Feist         }
34131058fd3SJosh Lehan 
34231058fd3SJosh Lehan         // Although this checks only the first vector element for the
34331058fd3SJosh Lehan         // named threshold, it is OK, because the SetPointOffset parser
34431058fd3SJosh Lehan         // splits up the input into individual vectors, each with only a
34531058fd3SJosh Lehan         // single element, if it detects that SetPointOffset is in use.
34631058fd3SJosh Lehan         const std::string& path =
34731058fd3SJosh Lehan             sensorConfig.at(info.inputs.front().name).readPath;
3485ec20270SJames Feist 
3498729eb98SPatrick Venture         DbusHelper helper(sdbusplus::bus::new_system());
3509b93692dSPatrick Venture         std::string service = helper.getService(interface, path);
3515ec20270SJames Feist         double reading = 0;
3525ec20270SJames Feist         try
3535ec20270SJames Feist         {
3549b93692dSPatrick Venture             helper.getProperty(service, path, interface, *thresholdProperty,
3559b93692dSPatrick Venture                                reading);
3565ec20270SJames Feist         }
357b228bc30SPatrick Williams         catch (const sdbusplus::exception_t& ex)
3585ec20270SJames Feist         {
3595ec20270SJames Feist             // unsupported threshold, leaving reading at 0
3605ec20270SJames Feist         }
3615ec20270SJames Feist 
3625ec20270SJames Feist         info.setpoint += reading;
3635ec20270SJames Feist     }
3645ec20270SJames Feist 
3655ec20270SJames Feist     info.pidInfo.ts = 1.0; // currently unused
3666fc301fbSJason Ling     info.pidInfo.proportionalCoeff = std::visit(
3676fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "PCoefficient"));
3686fc301fbSJason Ling     info.pidInfo.integralCoeff = std::visit(
3696fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ICoefficient"));
370c612c051SJosh Lehan     // DCoefficient is below, it is optional, same reason as in buildjson.cpp
3716fc301fbSJason Ling     info.pidInfo.feedFwdOffset = std::visit(
3726fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "FFOffCoefficient"));
3736fc301fbSJason Ling     info.pidInfo.feedFwdGain = std::visit(
3746fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "FFGainCoefficient"));
3756fc301fbSJason Ling     info.pidInfo.integralLimit.max = std::visit(
3766fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMax"));
3776fc301fbSJason Ling     info.pidInfo.integralLimit.min = std::visit(
3786fc301fbSJason Ling         VariantToDoubleVisitor(), getPIDAttribute(base, "ILimitMin"));
3796fc301fbSJason Ling     info.pidInfo.outLim.max = std::visit(VariantToDoubleVisitor(),
3806fc301fbSJason Ling                                          getPIDAttribute(base, "OutLimitMax"));
3816fc301fbSJason Ling     info.pidInfo.outLim.min = std::visit(VariantToDoubleVisitor(),
3826fc301fbSJason Ling                                          getPIDAttribute(base, "OutLimitMin"));
3838c051121SPatrick Williams     info.pidInfo.slewNeg = std::visit(VariantToDoubleVisitor(),
3848c051121SPatrick Williams                                       getPIDAttribute(base, "SlewNeg"));
3858c051121SPatrick Williams     info.pidInfo.slewPos = std::visit(VariantToDoubleVisitor(),
3868c051121SPatrick Williams                                       getPIDAttribute(base, "SlewPos"));
387c612c051SJosh Lehan 
3885ec20270SJames Feist     double negativeHysteresis = 0;
3895ec20270SJames Feist     double positiveHysteresis = 0;
390c612c051SJosh Lehan     double derivativeCoeff = 0;
3915ec20270SJames Feist 
3925ec20270SJames Feist     auto findNeg = base.find("NegativeHysteresis");
3935ec20270SJames Feist     auto findPos = base.find("PositiveHysteresis");
394c612c051SJosh Lehan     auto findDerivative = base.find("DCoefficient");
3955ec20270SJames Feist 
3965ec20270SJames Feist     if (findNeg != base.end())
3975ec20270SJames Feist     {
3988c051121SPatrick Williams         negativeHysteresis = std::visit(VariantToDoubleVisitor(),
3998c051121SPatrick Williams                                         findNeg->second);
4005ec20270SJames Feist     }
4015ec20270SJames Feist     if (findPos != base.end())
4025ec20270SJames Feist     {
4038c051121SPatrick Williams         positiveHysteresis = std::visit(VariantToDoubleVisitor(),
4048c051121SPatrick Williams                                         findPos->second);
4055ec20270SJames Feist     }
406c612c051SJosh Lehan     if (findDerivative != base.end())
407c612c051SJosh Lehan     {
4088c051121SPatrick Williams         derivativeCoeff = std::visit(VariantToDoubleVisitor(),
4098c051121SPatrick Williams                                      findDerivative->second);
410c612c051SJosh Lehan     }
411c612c051SJosh Lehan 
4125ec20270SJames Feist     info.pidInfo.negativeHysteresis = negativeHysteresis;
4135ec20270SJames Feist     info.pidInfo.positiveHysteresis = positiveHysteresis;
414c612c051SJosh Lehan     info.pidInfo.derivativeCoeff = derivativeCoeff;
4155ec20270SJames Feist }
4165ec20270SJames Feist 
417b228bc30SPatrick Williams bool init(sdbusplus::bus_t& bus, boost::asio::steady_timer& timer,
4187382318fSPatrick Venture           std::map<std::string, conf::SensorConfig>& sensorConfig,
4197382318fSPatrick Venture           std::map<int64_t, conf::PIDConf>& zoneConfig,
4207382318fSPatrick Venture           std::map<int64_t, conf::ZoneConfig>& zoneDetailsConfig)
4211fe08952SJames Feist {
4221fe08952SJames Feist     sensorConfig.clear();
4231fe08952SJames Feist     zoneConfig.clear();
4241fe08952SJames Feist     zoneDetailsConfig.clear();
4251fe08952SJames Feist 
4261fe08952SJames Feist     createMatches(bus, timer);
4271fe08952SJames Feist 
4288c051121SPatrick Williams     auto mapper = bus.new_method_call("xyz.openbmc_project.ObjectMapper",
4297136a5aeSJames Feist                                       "/xyz/openbmc_project/object_mapper",
4308c051121SPatrick Williams                                       "xyz.openbmc_project.ObjectMapper",
4318c051121SPatrick Williams                                       "GetSubTree");
43226e8c6a9SJames Feist     mapper.append("/", 0,
4330911bfe7SPatrick Venture                   std::array<const char*, 6>{
4340911bfe7SPatrick Venture                       objectManagerInterface, pidConfigurationInterface,
4357136a5aeSJames Feist                       pidZoneConfigurationInterface,
4360911bfe7SPatrick Venture                       stepwiseConfigurationInterface, sensorInterface,
4370911bfe7SPatrick Venture                       defaultPwmInterface});
43822c257abSJames Feist     std::unordered_map<
43922c257abSJames Feist         std::string, std::unordered_map<std::string, std::vector<std::string>>>
44022c257abSJames Feist         respData;
44122c257abSJames Feist     try
44222c257abSJames Feist     {
4437136a5aeSJames Feist         auto resp = bus.call(mapper);
4447136a5aeSJames Feist         resp.read(respData);
44522c257abSJames Feist     }
4460001ee02SPatrick Williams     catch (const sdbusplus::exception_t&)
44722c257abSJames Feist     {
44822c257abSJames Feist         // can't do anything without mapper call data
44922c257abSJames Feist         throw std::runtime_error("ObjectMapper Call Failure");
45022c257abSJames Feist     }
45122c257abSJames Feist 
4527136a5aeSJames Feist     if (respData.empty())
4537136a5aeSJames Feist     {
45422c257abSJames Feist         // can't do anything without mapper call data
4557136a5aeSJames Feist         throw std::runtime_error("No configuration data available from Mapper");
4567136a5aeSJames Feist     }
4577136a5aeSJames Feist     // create a map of pair of <has pid configuration, ObjectManager path>
4587136a5aeSJames Feist     std::unordered_map<std::string, std::pair<bool, std::string>> owners;
4597136a5aeSJames Feist     // and a map of <path, interface> for sensors
4607136a5aeSJames Feist     std::unordered_map<std::string, std::string> sensors;
4617136a5aeSJames Feist     for (const auto& objectPair : respData)
4627136a5aeSJames Feist     {
4637136a5aeSJames Feist         for (const auto& ownerPair : objectPair.second)
4647136a5aeSJames Feist         {
4657136a5aeSJames Feist             auto& owner = owners[ownerPair.first];
4667136a5aeSJames Feist             for (const std::string& interface : ownerPair.second)
4677136a5aeSJames Feist             {
4687136a5aeSJames Feist                 if (interface == objectManagerInterface)
4697136a5aeSJames Feist                 {
4707136a5aeSJames Feist                     owner.second = objectPair.first;
4717136a5aeSJames Feist                 }
4727136a5aeSJames Feist                 if (interface == pidConfigurationInterface ||
47322c257abSJames Feist                     interface == pidZoneConfigurationInterface ||
47422c257abSJames Feist                     interface == stepwiseConfigurationInterface)
4757136a5aeSJames Feist                 {
4767136a5aeSJames Feist                     owner.first = true;
4777136a5aeSJames Feist                 }
4780911bfe7SPatrick Venture                 if (interface == sensorInterface ||
4790911bfe7SPatrick Venture                     interface == defaultPwmInterface)
4807136a5aeSJames Feist                 {
4817136a5aeSJames Feist                     // we're not interested in pwm sensors, just pwm control
4827136a5aeSJames Feist                     if (interface == sensorInterface &&
4837136a5aeSJames Feist                         objectPair.first.find("pwm") != std::string::npos)
4847136a5aeSJames Feist                     {
4857136a5aeSJames Feist                         continue;
4867136a5aeSJames Feist                     }
4877136a5aeSJames Feist                     sensors[objectPair.first] = interface;
4887136a5aeSJames Feist                 }
4897136a5aeSJames Feist             }
4907136a5aeSJames Feist         }
4917136a5aeSJames Feist     }
4927136a5aeSJames Feist     ManagedObjectType configurations;
4937136a5aeSJames Feist     for (const auto& owner : owners)
4947136a5aeSJames Feist     {
4957136a5aeSJames Feist         // skip if no pid configuration (means probably a sensor)
4967136a5aeSJames Feist         if (!owner.second.first)
4977136a5aeSJames Feist         {
4987136a5aeSJames Feist             continue;
4997136a5aeSJames Feist         }
5007136a5aeSJames Feist         auto endpoint = bus.new_method_call(
5017136a5aeSJames Feist             owner.first.c_str(), owner.second.second.c_str(),
5027136a5aeSJames Feist             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
50322c257abSJames Feist         ManagedObjectType configuration;
50422c257abSJames Feist         try
50522c257abSJames Feist         {
5067136a5aeSJames Feist             auto responce = bus.call(endpoint);
5077136a5aeSJames Feist             responce.read(configuration);
50822c257abSJames Feist         }
5090001ee02SPatrick Williams         catch (const sdbusplus::exception_t&)
51022c257abSJames Feist         {
51122c257abSJames Feist             // this shouldn't happen, probably means daemon crashed
51222c257abSJames Feist             throw std::runtime_error("Error getting managed objects from " +
51322c257abSJames Feist                                      owner.first);
51422c257abSJames Feist         }
51522c257abSJames Feist 
5167136a5aeSJames Feist         for (auto& pathPair : configuration)
5177136a5aeSJames Feist         {
5187136a5aeSJames Feist             if (pathPair.second.find(pidConfigurationInterface) !=
5197136a5aeSJames Feist                     pathPair.second.end() ||
5207136a5aeSJames Feist                 pathPair.second.find(pidZoneConfigurationInterface) !=
52122c257abSJames Feist                     pathPair.second.end() ||
52222c257abSJames Feist                 pathPair.second.find(stepwiseConfigurationInterface) !=
5237136a5aeSJames Feist                     pathPair.second.end())
5247136a5aeSJames Feist             {
5257136a5aeSJames Feist                 configurations.emplace(pathPair);
5267136a5aeSJames Feist             }
527f0096a0cSJames Feist         }
528f0096a0cSJames Feist     }
529f0096a0cSJames Feist 
530f0096a0cSJames Feist     // remove controllers from config that aren't in the current profile(s)
531f0096a0cSJames Feist     std::vector<std::string> selectedProfiles = getSelectedProfiles(bus);
532f0096a0cSJames Feist     if (selectedProfiles.size())
533f0096a0cSJames Feist     {
5343987c8b4SJames Feist         for (auto pathIt = configurations.begin();
5353987c8b4SJames Feist              pathIt != configurations.end();)
536f0096a0cSJames Feist         {
5373987c8b4SJames Feist             for (auto confIt = pathIt->second.begin();
5383987c8b4SJames Feist                  confIt != pathIt->second.end();)
5393987c8b4SJames Feist             {
5403987c8b4SJames Feist                 auto profilesFind = confIt->second.find("Profiles");
5413987c8b4SJames Feist                 if (profilesFind == confIt->second.end())
5423987c8b4SJames Feist                 {
5433987c8b4SJames Feist                     confIt++;
5443987c8b4SJames Feist                     continue; // if no profiles selected, apply always
5453987c8b4SJames Feist                 }
5463987c8b4SJames Feist                 auto profiles =
5473987c8b4SJames Feist                     std::get<std::vector<std::string>>(profilesFind->second);
5483987c8b4SJames Feist                 if (profiles.empty())
5493987c8b4SJames Feist                 {
5503987c8b4SJames Feist                     confIt++;
5513987c8b4SJames Feist                     continue;
552f0096a0cSJames Feist                 }
553f0096a0cSJames Feist 
5543987c8b4SJames Feist                 bool found = false;
5553987c8b4SJames Feist                 for (const std::string& profile : profiles)
556f0096a0cSJames Feist                 {
5573987c8b4SJames Feist                     if (std::find(selectedProfiles.begin(),
5583987c8b4SJames Feist                                   selectedProfiles.end(),
5593987c8b4SJames Feist                                   profile) != selectedProfiles.end())
560f0096a0cSJames Feist                     {
5613987c8b4SJames Feist                         found = true;
5623987c8b4SJames Feist                         break;
5633987c8b4SJames Feist                     }
5643987c8b4SJames Feist                 }
5653987c8b4SJames Feist                 if (found)
5663987c8b4SJames Feist                 {
5673987c8b4SJames Feist                     confIt++;
568f0096a0cSJames Feist                 }
569f0096a0cSJames Feist                 else
570f0096a0cSJames Feist                 {
5713987c8b4SJames Feist                     confIt = pathIt->second.erase(confIt);
572f0096a0cSJames Feist                 }
573f0096a0cSJames Feist             }
5743987c8b4SJames Feist             if (pathIt->second.empty())
575f0096a0cSJames Feist             {
5763987c8b4SJames Feist                 pathIt = configurations.erase(pathIt);
577f0096a0cSJames Feist             }
578f0096a0cSJames Feist             else
579f0096a0cSJames Feist             {
5803987c8b4SJames Feist                 pathIt++;
581f0096a0cSJames Feist             }
5827136a5aeSJames Feist         }
5837136a5aeSJames Feist     }
5848c3c51eeSJames Feist 
585998fbe67SJosh Lehan     // On D-Bus, although not necessary,
586998fbe67SJosh Lehan     // having the "zoneID" field can still be useful,
587998fbe67SJosh Lehan     // as it is used for diagnostic messages,
588998fbe67SJosh Lehan     // logging file names, and so on.
589998fbe67SJosh Lehan     // Accept optional "ZoneIndex" parameter to explicitly specify.
590998fbe67SJosh Lehan     // If not present, or not unique, auto-assign index,
591998fbe67SJosh Lehan     // using 0-based numbering, ensuring uniqueness.
592998fbe67SJosh Lehan     std::map<std::string, int64_t> foundZones;
5937136a5aeSJames Feist     for (const auto& configuration : configurations)
5947136a5aeSJames Feist     {
5957136a5aeSJames Feist         auto findZone =
5967136a5aeSJames Feist             configuration.second.find(pidZoneConfigurationInterface);
5977136a5aeSJames Feist         if (findZone != configuration.second.end())
5987136a5aeSJames Feist         {
5997136a5aeSJames Feist             const auto& zone = findZone->second;
600ffd418bbSJames Feist 
6011f802f5eSJames Feist             const std::string& name = std::get<std::string>(zone.at("Name"));
602998fbe67SJosh Lehan 
603998fbe67SJosh Lehan             auto findZoneIndex = zone.find("ZoneIndex");
604998fbe67SJosh Lehan             if (findZoneIndex == zone.end())
605998fbe67SJosh Lehan             {
606998fbe67SJosh Lehan                 continue;
607998fbe67SJosh Lehan             }
608998fbe67SJosh Lehan 
609998fbe67SJosh Lehan             auto ptrZoneIndex = std::get_if<double>(&(findZoneIndex->second));
610998fbe67SJosh Lehan             if (!ptrZoneIndex)
611998fbe67SJosh Lehan             {
612998fbe67SJosh Lehan                 continue;
613998fbe67SJosh Lehan             }
614998fbe67SJosh Lehan 
615998fbe67SJosh Lehan             auto desiredIndex = static_cast<int64_t>(*ptrZoneIndex);
616998fbe67SJosh Lehan             auto grantedIndex = setZoneIndex(name, foundZones, desiredIndex);
617998fbe67SJosh Lehan             std::cout << "Zone " << name << " is at ZoneIndex " << grantedIndex
618998fbe67SJosh Lehan                       << "\n";
619998fbe67SJosh Lehan         }
620998fbe67SJosh Lehan     }
621998fbe67SJosh Lehan 
622998fbe67SJosh Lehan     for (const auto& configuration : configurations)
623998fbe67SJosh Lehan     {
624998fbe67SJosh Lehan         auto findZone =
625998fbe67SJosh Lehan             configuration.second.find(pidZoneConfigurationInterface);
626998fbe67SJosh Lehan         if (findZone != configuration.second.end())
627998fbe67SJosh Lehan         {
628998fbe67SJosh Lehan             const auto& zone = findZone->second;
629998fbe67SJosh Lehan 
630998fbe67SJosh Lehan             const std::string& name = std::get<std::string>(zone.at("Name"));
631998fbe67SJosh Lehan 
632998fbe67SJosh Lehan             auto index = getZoneIndex(name, foundZones);
6338c3c51eeSJames Feist 
634c54fbd88SPatrick Venture             auto& details = zoneDetailsConfig[index];
635998fbe67SJosh Lehan 
6363484bedaSJames Feist             details.minThermalOutput = std::visit(VariantToDoubleVisitor(),
6373484bedaSJames Feist                                                   zone.at("MinThermalOutput"));
6389fe3a3c7Sykchiu 
6399fe3a3c7Sykchiu             int failsafepercent = 0;
6409fe3a3c7Sykchiu             auto findFailSafe = zone.find("FailSafePercent");
6419fe3a3c7Sykchiu             if (findFailSafe != zone.end())
6429fe3a3c7Sykchiu             {
6439fe3a3c7Sykchiu                 failsafepercent = std::visit(VariantToDoubleVisitor(),
6441f802f5eSJames Feist                                              zone.at("FailSafePercent"));
6459fe3a3c7Sykchiu             }
6469fe3a3c7Sykchiu             details.failsafePercent = failsafepercent;
6479f9a06aaSJosh Lehan 
648239aa7d7SHarvey Wu             getCycleTimeSetting(zone, index, "CycleIntervalTimeMS",
649239aa7d7SHarvey Wu                                 details.cycleTime.cycleIntervalTimeMS);
650239aa7d7SHarvey Wu             getCycleTimeSetting(zone, index, "UpdateThermalsTimeMS",
651239aa7d7SHarvey Wu                                 details.cycleTime.updateThermalsTimeMS);
6527136a5aeSJames Feist         }
6537136a5aeSJames Feist         auto findBase = configuration.second.find(pidConfigurationInterface);
654f3b04fd3SJason Ling         // loop through all the PID configurations and fill out a sensor config
65522c257abSJames Feist         if (findBase != configuration.second.end())
6567136a5aeSJames Feist         {
65722c257abSJames Feist             const auto& base =
65822c257abSJames Feist                 configuration.second.at(pidConfigurationInterface);
6597c6d35d5Sykchiu             const std::string pidName =
6607c6d35d5Sykchiu                 sensorNameToDbusName(std::get<std::string>(base.at("Name")));
661f3b04fd3SJason Ling             const std::string pidClass =
662f3b04fd3SJason Ling                 std::get<std::string>(base.at("Class"));
6638c3c51eeSJames Feist             const std::vector<std::string>& zones =
6641f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
6658c3c51eeSJames Feist             for (const std::string& zone : zones)
6668c3c51eeSJames Feist             {
667998fbe67SJosh Lehan                 auto index = getZoneIndex(zone, foundZones);
668998fbe67SJosh Lehan 
669f81f2886SJames Feist                 conf::PIDConf& conf = zoneConfig[index];
670f3b04fd3SJason Ling                 std::vector<std::string> inputSensorNames(
671f3b04fd3SJason Ling                     std::get<std::vector<std::string>>(base.at("Inputs")));
672f3b04fd3SJason Ling                 std::vector<std::string> outputSensorNames;
673*3f0f7bc3SJosh Lehan                 std::vector<std::string> missingAcceptableSensorNames;
674*3f0f7bc3SJosh Lehan 
675*3f0f7bc3SJosh Lehan                 auto findMissingAcceptable = base.find("MissingIsAcceptable");
676*3f0f7bc3SJosh Lehan                 if (findMissingAcceptable != base.end())
677*3f0f7bc3SJosh Lehan                 {
678*3f0f7bc3SJosh Lehan                     missingAcceptableSensorNames =
679*3f0f7bc3SJosh Lehan                         std::get<std::vector<std::string>>(
680*3f0f7bc3SJosh Lehan                             findMissingAcceptable->second);
681*3f0f7bc3SJosh Lehan                 }
68250fdfe39SJames Feist 
683f3b04fd3SJason Ling                 // assumption: all fan pids must have at least one output
684f3b04fd3SJason Ling                 if (pidClass == "fan")
68550fdfe39SJames Feist                 {
686f3b04fd3SJason Ling                     outputSensorNames = std::get<std::vector<std::string>>(
687f3b04fd3SJason Ling                         getPIDAttribute(base, "Outputs"));
68850fdfe39SJames Feist                 }
6891738e2a2SJames Feist 
6908f73ad76SAlex.Song                 bool unavailableAsFailed = true;
6918f73ad76SAlex.Song                 auto findUnavailableAsFailed =
6928f73ad76SAlex.Song                     base.find("InputUnavailableAsFailed");
6938f73ad76SAlex.Song                 if (findUnavailableAsFailed != base.end())
6948f73ad76SAlex.Song                 {
6958f73ad76SAlex.Song                     unavailableAsFailed =
6968f73ad76SAlex.Song                         std::get<bool>(findUnavailableAsFailed->second);
6978f73ad76SAlex.Song                 }
6988f73ad76SAlex.Song 
699f3b04fd3SJason Ling                 std::vector<SensorInterfaceType> inputSensorInterfaces;
700f3b04fd3SJason Ling                 std::vector<SensorInterfaceType> outputSensorInterfaces;
701*3f0f7bc3SJosh Lehan                 std::vector<SensorInterfaceType>
702*3f0f7bc3SJosh Lehan                     missingAcceptableSensorInterfaces;
703*3f0f7bc3SJosh Lehan 
704f3b04fd3SJason Ling                 /* populate an interface list for different sensor direction
705f3b04fd3SJason Ling                  * types (input,output)
706f3b04fd3SJason Ling                  */
707f3b04fd3SJason Ling                 /* take the Inputs from the configuration and generate
708f3b04fd3SJason Ling                  * a list of dbus descriptors (path, interface).
709f3b04fd3SJason Ling                  * Mapping can be many-to-one since an element of Inputs can be
710f3b04fd3SJason Ling                  * a regex
711f3b04fd3SJason Ling                  */
712f3b04fd3SJason Ling                 for (const std::string& sensorName : inputSensorNames)
71350fdfe39SJames Feist                 {
714f3b04fd3SJason Ling                     findSensors(sensors, sensorNameToDbusName(sensorName),
715f3b04fd3SJason Ling                                 inputSensorInterfaces);
716f3b04fd3SJason Ling                 }
717f3b04fd3SJason Ling                 for (const std::string& sensorName : outputSensorNames)
718f3b04fd3SJason Ling                 {
719f3b04fd3SJason Ling                     findSensors(sensors, sensorNameToDbusName(sensorName),
720f3b04fd3SJason Ling                                 outputSensorInterfaces);
72150fdfe39SJames Feist                 }
722*3f0f7bc3SJosh Lehan                 for (const std::string& sensorName :
723*3f0f7bc3SJosh Lehan                      missingAcceptableSensorNames)
724*3f0f7bc3SJosh Lehan                 {
725*3f0f7bc3SJosh Lehan                     findSensors(sensors, sensorNameToDbusName(sensorName),
726*3f0f7bc3SJosh Lehan                                 missingAcceptableSensorInterfaces);
727*3f0f7bc3SJosh Lehan                 }
7281738e2a2SJames Feist 
729f3b04fd3SJason Ling                 inputSensorNames.clear();
730f3b04fd3SJason Ling                 for (const SensorInterfaceType& inputSensorInterface :
731f3b04fd3SJason Ling                      inputSensorInterfaces)
7321738e2a2SJames Feist                 {
733f3b04fd3SJason Ling                     const std::string& dbusInterface =
734f3b04fd3SJason Ling                         inputSensorInterface.second;
735f3b04fd3SJason Ling                     const std::string& inputSensorPath =
736f3b04fd3SJason Ling                         inputSensorInterface.first;
737fb82a87dSJosh Lehan 
738fb82a87dSJosh Lehan                     // Setting timeout to 0 is intentional, as D-Bus passive
739fb82a87dSJosh Lehan                     // sensor updates are pushed in, not pulled by timer poll.
740fb82a87dSJosh Lehan                     // Setting ignoreDbusMinMax is intentional, as this
741fb82a87dSJosh Lehan                     // prevents normalization of values to [0.0, 1.0] range,
742fb82a87dSJosh Lehan                     // which would mess up the PID loop math.
743fb82a87dSJosh Lehan                     // All non-fan PID classes should be initialized this way.
744fb82a87dSJosh Lehan                     // As for why a fan should not use this code path, see
745fb82a87dSJosh Lehan                     // the ed1dafdf168def37c65bfb7a5efd18d9dbe04727 commit.
74623e22b90SJosh Lehan                     if ((pidClass == "temp") || (pidClass == "margin") ||
74723e22b90SJosh Lehan                         (pidClass == "power") || (pidClass == "powersum"))
748ed1dafdfSHarvey.Wu                     {
749f3b04fd3SJason Ling                         std::string inputSensorName =
750f3b04fd3SJason Ling                             getSensorNameFromPath(inputSensorPath);
751f3b04fd3SJason Ling                         auto& config = sensorConfig[inputSensorName];
752f3b04fd3SJason Ling                         inputSensorNames.push_back(inputSensorName);
753f3b04fd3SJason Ling                         config.type = pidClass;
754f3b04fd3SJason Ling                         config.readPath = inputSensorInterface.first;
7552642cb54SJames Feist                         config.timeout = 0;
7563433cb60SJames Feist                         config.ignoreDbusMinMax = true;
7578f73ad76SAlex.Song                         config.unavailableAsFailed = unavailableAsFailed;
75850fdfe39SJames Feist                     }
759fb82a87dSJosh Lehan 
760f3b04fd3SJason Ling                     if (dbusInterface != sensorInterface)
761f3b04fd3SJason Ling                     {
762f3b04fd3SJason Ling                         /* all expected inputs in the configuration are expected
763f3b04fd3SJason Ling                          * to be sensor interfaces
764f3b04fd3SJason Ling                          */
765f3b04fd3SJason Ling                         throw std::runtime_error(
766f3b04fd3SJason Ling                             "sensor at dbus path [" + inputSensorPath +
767f3b04fd3SJason Ling                             "] has an interface [" + dbusInterface +
768f3b04fd3SJason Ling                             "] that does not match the expected interface of " +
769f3b04fd3SJason Ling                             sensorInterface);
77050fdfe39SJames Feist                     }
77150fdfe39SJames Feist                 }
7721738e2a2SJames Feist 
773*3f0f7bc3SJosh Lehan                 // MissingIsAcceptable same postprocessing as Inputs
774*3f0f7bc3SJosh Lehan                 missingAcceptableSensorNames.clear();
775*3f0f7bc3SJosh Lehan                 for (const SensorInterfaceType&
776*3f0f7bc3SJosh Lehan                          missingAcceptableSensorInterface :
777*3f0f7bc3SJosh Lehan                      missingAcceptableSensorInterfaces)
778*3f0f7bc3SJosh Lehan                 {
779*3f0f7bc3SJosh Lehan                     const std::string& dbusInterface =
780*3f0f7bc3SJosh Lehan                         missingAcceptableSensorInterface.second;
781*3f0f7bc3SJosh Lehan                     const std::string& missingAcceptableSensorPath =
782*3f0f7bc3SJosh Lehan                         missingAcceptableSensorInterface.first;
783*3f0f7bc3SJosh Lehan 
784*3f0f7bc3SJosh Lehan                     std::string missingAcceptableSensorName =
785*3f0f7bc3SJosh Lehan                         getSensorNameFromPath(missingAcceptableSensorPath);
786*3f0f7bc3SJosh Lehan                     missingAcceptableSensorNames.push_back(
787*3f0f7bc3SJosh Lehan                         missingAcceptableSensorName);
788*3f0f7bc3SJosh Lehan 
789*3f0f7bc3SJosh Lehan                     if (dbusInterface != sensorInterface)
790*3f0f7bc3SJosh Lehan                     {
791*3f0f7bc3SJosh Lehan                         /* MissingIsAcceptable same error checking as Inputs
792*3f0f7bc3SJosh Lehan                          */
793*3f0f7bc3SJosh Lehan                         throw std::runtime_error(
794*3f0f7bc3SJosh Lehan                             "sensor at dbus path [" +
795*3f0f7bc3SJosh Lehan                             missingAcceptableSensorPath +
796*3f0f7bc3SJosh Lehan                             "] has an interface [" + dbusInterface +
797*3f0f7bc3SJosh Lehan                             "] that does not match the expected interface of " +
798*3f0f7bc3SJosh Lehan                             sensorInterface);
799*3f0f7bc3SJosh Lehan                     }
800*3f0f7bc3SJosh Lehan                 }
801*3f0f7bc3SJosh Lehan 
802f3b04fd3SJason Ling                 /* fan pids need to pair up tach sensors with their pwm
803f3b04fd3SJason Ling                  * counterparts
804f3b04fd3SJason Ling                  */
805f3b04fd3SJason Ling                 if (pidClass == "fan")
806f3b04fd3SJason Ling                 {
807f3b04fd3SJason Ling                     /* If a PID is a fan there should be either
808f3b04fd3SJason Ling                      * (1) one output(pwm) per input(tach)
809f3b04fd3SJason Ling                      * OR
810f3b04fd3SJason Ling                      * (2) one putput(pwm) for all inputs(tach)
811f3b04fd3SJason Ling                      * everything else indicates a bad configuration.
812f3b04fd3SJason Ling                      */
813f3b04fd3SJason Ling                     bool singlePwm = false;
814f3b04fd3SJason Ling                     if (outputSensorInterfaces.size() == 1)
815f3b04fd3SJason Ling                     {
816f3b04fd3SJason Ling                         /* one pwm, set write paths for all fan sensors to it */
817f3b04fd3SJason Ling                         singlePwm = true;
818f3b04fd3SJason Ling                     }
819f3b04fd3SJason Ling                     else if (inputSensorInterfaces.size() ==
820f3b04fd3SJason Ling                              outputSensorInterfaces.size())
821f3b04fd3SJason Ling                     {
822f3b04fd3SJason Ling                         /* one to one mapping, each fan sensor gets its own pwm
823f3b04fd3SJason Ling                          * control */
824f3b04fd3SJason Ling                         singlePwm = false;
825f3b04fd3SJason Ling                     }
826f3b04fd3SJason Ling                     else
827f3b04fd3SJason Ling                     {
828f3b04fd3SJason Ling                         throw std::runtime_error(
829f3b04fd3SJason Ling                             "fan PID has invalid number of Outputs");
830f3b04fd3SJason Ling                     }
831f3b04fd3SJason Ling                     std::string fanSensorName;
832f3b04fd3SJason Ling                     std::string pwmPath;
833f3b04fd3SJason Ling                     std::string pwmInterface;
834ed1dafdfSHarvey.Wu                     std::string pwmSensorName;
835f3b04fd3SJason Ling                     if (singlePwm)
836f3b04fd3SJason Ling                     {
837f3b04fd3SJason Ling                         /* if just a single output(pwm) is provided then use
838f3b04fd3SJason Ling                          * that pwm control path for all the fan sensor write
839f3b04fd3SJason Ling                          * path configs
840f3b04fd3SJason Ling                          */
841f3b04fd3SJason Ling                         pwmPath = outputSensorInterfaces.at(0).first;
842f3b04fd3SJason Ling                         pwmInterface = outputSensorInterfaces.at(0).second;
843f3b04fd3SJason Ling                     }
844f3b04fd3SJason Ling                     for (uint32_t idx = 0; idx < inputSensorInterfaces.size();
845f3b04fd3SJason Ling                          idx++)
846f3b04fd3SJason Ling                     {
847f3b04fd3SJason Ling                         if (!singlePwm)
848f3b04fd3SJason Ling                         {
849f3b04fd3SJason Ling                             pwmPath = outputSensorInterfaces.at(idx).first;
850f3b04fd3SJason Ling                             pwmInterface =
851f3b04fd3SJason Ling                                 outputSensorInterfaces.at(idx).second;
852f3b04fd3SJason Ling                         }
8530911bfe7SPatrick Venture                         if (defaultPwmInterface != pwmInterface)
854f3b04fd3SJason Ling                         {
855f3b04fd3SJason Ling                             throw std::runtime_error(
856f3b04fd3SJason Ling                                 "fan pwm control at dbus path [" + pwmPath +
857f3b04fd3SJason Ling                                 "] has an interface [" + pwmInterface +
858f3b04fd3SJason Ling                                 "] that does not match the expected interface "
859f3b04fd3SJason Ling                                 "of " +
8600911bfe7SPatrick Venture                                 defaultPwmInterface);
861f3b04fd3SJason Ling                         }
862f3b04fd3SJason Ling                         const std::string& fanPath =
863f3b04fd3SJason Ling                             inputSensorInterfaces.at(idx).first;
864f3b04fd3SJason Ling                         fanSensorName = getSensorNameFromPath(fanPath);
865ed1dafdfSHarvey.Wu                         pwmSensorName = getSensorNameFromPath(pwmPath);
866ed1dafdfSHarvey.Wu                         std::string fanPwmIndex = fanSensorName + pwmSensorName;
867ed1dafdfSHarvey.Wu                         inputSensorNames.push_back(fanPwmIndex);
868ed1dafdfSHarvey.Wu                         auto& fanConfig = sensorConfig[fanPwmIndex];
869ed1dafdfSHarvey.Wu                         fanConfig.type = pidClass;
870ed1dafdfSHarvey.Wu                         fanConfig.readPath = fanPath;
871f3b04fd3SJason Ling                         fanConfig.writePath = pwmPath;
87250fdfe39SJames Feist                         // todo: un-hardcode this if there are fans with
87350fdfe39SJames Feist                         // different ranges
874f3b04fd3SJason Ling                         fanConfig.max = 255;
875f3b04fd3SJason Ling                         fanConfig.min = 0;
87650fdfe39SJames Feist                     }
87750fdfe39SJames Feist                 }
87811d243dfSJames Feist                 // if the sensors aren't available in the current state, don't
87911d243dfSJames Feist                 // add them to the configuration.
880f3b04fd3SJason Ling                 if (inputSensorNames.empty())
88111d243dfSJames Feist                 {
88211d243dfSJames Feist                     continue;
88311d243dfSJames Feist                 }
88411d243dfSJames Feist 
8855ec20270SJames Feist                 std::string offsetType;
8865ec20270SJames Feist 
8875ec20270SJames Feist                 // SetPointOffset is a threshold value to pull from the sensor
8885ec20270SJames Feist                 // to apply an offset. For upper thresholds this means the
8895ec20270SJames Feist                 // setpoint is usually negative.
8905ec20270SJames Feist                 auto findSetpointOffset = base.find("SetPointOffset");
8915ec20270SJames Feist                 if (findSetpointOffset != base.end())
8925ec20270SJames Feist                 {
8935ec20270SJames Feist                     offsetType =
8945ec20270SJames Feist                         std::get<std::string>(findSetpointOffset->second);
8955ec20270SJames Feist                     if (std::find(thresholds::types.begin(),
8965ec20270SJames Feist                                   thresholds::types.end(),
8975ec20270SJames Feist                                   offsetType) == thresholds::types.end())
8985ec20270SJames Feist                     {
8995ec20270SJames Feist                         throw std::runtime_error("Unsupported type: " +
9005ec20270SJames Feist                                                  offsetType);
9015ec20270SJames Feist                     }
9025ec20270SJames Feist                 }
9035ec20270SJames Feist 
90431058fd3SJosh Lehan                 std::vector<double> inputTempToMargin;
90531058fd3SJosh Lehan 
90631058fd3SJosh Lehan                 auto findTempToMargin = base.find("TempToMargin");
90731058fd3SJosh Lehan                 if (findTempToMargin != base.end())
90831058fd3SJosh Lehan                 {
90931058fd3SJosh Lehan                     inputTempToMargin =
91031058fd3SJosh Lehan                         std::get<std::vector<double>>(findTempToMargin->second);
91131058fd3SJosh Lehan                 }
91231058fd3SJosh Lehan 
91331058fd3SJosh Lehan                 std::vector<pid_control::conf::SensorInput> sensorInputs =
914*3f0f7bc3SJosh Lehan                     spliceInputs(inputSensorNames, inputTempToMargin,
915*3f0f7bc3SJosh Lehan                                  missingAcceptableSensorNames);
91631058fd3SJosh Lehan 
9175ec20270SJames Feist                 if (offsetType.empty())
9185ec20270SJames Feist                 {
9197c6d35d5Sykchiu                     conf::ControllerInfo& info = conf[pidName];
92031058fd3SJosh Lehan                     info.inputs = std::move(sensorInputs);
9217382318fSPatrick Venture                     populatePidInfo(bus, base, info, nullptr, sensorConfig);
9227136a5aeSJames Feist                 }
9237136a5aeSJames Feist                 else
9247136a5aeSJames Feist                 {
9255ec20270SJames Feist                     // we have to split up the inputs, as in practice t-control
9265ec20270SJames Feist                     // values will differ, making setpoints differ
92731058fd3SJosh Lehan                     for (const pid_control::conf::SensorInput& input :
92831058fd3SJosh Lehan                          sensorInputs)
929572c43daSJames Feist                     {
93031058fd3SJosh Lehan                         conf::ControllerInfo& info = conf[input.name];
9315ec20270SJames Feist                         info.inputs.emplace_back(input);
9327382318fSPatrick Venture                         populatePidInfo(bus, base, info, &offsetType,
9337382318fSPatrick Venture                                         sensorConfig);
934572c43daSJames Feist                     }
935572c43daSJames Feist                 }
9367136a5aeSJames Feist             }
9378c3c51eeSJames Feist         }
93822c257abSJames Feist         auto findStepwise =
93922c257abSJames Feist             configuration.second.find(stepwiseConfigurationInterface);
94022c257abSJames Feist         if (findStepwise != configuration.second.end())
94122c257abSJames Feist         {
94222c257abSJames Feist             const auto& base = findStepwise->second;
9437c6d35d5Sykchiu             const std::string pidName =
9447c6d35d5Sykchiu                 sensorNameToDbusName(std::get<std::string>(base.at("Name")));
94522c257abSJames Feist             const std::vector<std::string>& zones =
9461f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
94722c257abSJames Feist             for (const std::string& zone : zones)
94822c257abSJames Feist             {
949998fbe67SJosh Lehan                 auto index = getZoneIndex(zone, foundZones);
950998fbe67SJosh Lehan 
951f81f2886SJames Feist                 conf::PIDConf& conf = zoneConfig[index];
95250fdfe39SJames Feist 
95350fdfe39SJames Feist                 std::vector<std::string> inputs;
954*3f0f7bc3SJosh Lehan                 std::vector<std::string> missingAcceptableSensors;
955*3f0f7bc3SJosh Lehan                 std::vector<std::string> missingAcceptableSensorNames;
95650fdfe39SJames Feist                 std::vector<std::string> sensorNames =
9571f802f5eSJames Feist                     std::get<std::vector<std::string>>(base.at("Inputs"));
95850fdfe39SJames Feist 
959*3f0f7bc3SJosh Lehan                 auto findMissingAcceptable = base.find("MissingIsAcceptable");
960*3f0f7bc3SJosh Lehan                 if (findMissingAcceptable != base.end())
961*3f0f7bc3SJosh Lehan                 {
962*3f0f7bc3SJosh Lehan                     missingAcceptableSensorNames =
963*3f0f7bc3SJosh Lehan                         std::get<std::vector<std::string>>(
964*3f0f7bc3SJosh Lehan                             findMissingAcceptable->second);
965*3f0f7bc3SJosh Lehan                 }
966*3f0f7bc3SJosh Lehan 
9678f73ad76SAlex.Song                 bool unavailableAsFailed = true;
9688f73ad76SAlex.Song                 auto findUnavailableAsFailed =
9698f73ad76SAlex.Song                     base.find("InputUnavailableAsFailed");
9708f73ad76SAlex.Song                 if (findUnavailableAsFailed != base.end())
9718f73ad76SAlex.Song                 {
9728f73ad76SAlex.Song                     unavailableAsFailed =
9738f73ad76SAlex.Song                         std::get<bool>(findUnavailableAsFailed->second);
9748f73ad76SAlex.Song                 }
9758f73ad76SAlex.Song 
9761738e2a2SJames Feist                 bool sensorFound = false;
97750fdfe39SJames Feist                 for (const std::string& sensorName : sensorNames)
97850fdfe39SJames Feist                 {
9791738e2a2SJames Feist                     std::vector<std::pair<std::string, std::string>>
9801738e2a2SJames Feist                         sensorPathIfacePairs;
981f3b04fd3SJason Ling                     if (!findSensors(sensors, sensorNameToDbusName(sensorName),
982f3b04fd3SJason Ling                                      sensorPathIfacePairs))
98350fdfe39SJames Feist                     {
98450fdfe39SJames Feist                         break;
98550fdfe39SJames Feist                     }
98650fdfe39SJames Feist 
9871738e2a2SJames Feist                     for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
9881738e2a2SJames Feist                     {
9891738e2a2SJames Feist                         std::string shortName =
990*3f0f7bc3SJosh Lehan                             getSensorNameFromPath(sensorPathIfacePair.first);
9911738e2a2SJames Feist 
9921738e2a2SJames Feist                         inputs.push_back(shortName);
9931738e2a2SJames Feist                         auto& config = sensorConfig[shortName];
99469c51061SPatrick Venture                         config.readPath = sensorPathIfacePair.first;
99550fdfe39SJames Feist                         config.type = "temp";
9963660b388SJames Feist                         config.ignoreDbusMinMax = true;
9978f73ad76SAlex.Song                         config.unavailableAsFailed = unavailableAsFailed;
99850fdfe39SJames Feist                         // todo: maybe un-hardcode this if we run into slower
99950fdfe39SJames Feist                         // timeouts with sensors
100050fdfe39SJames Feist 
10012642cb54SJames Feist                         config.timeout = 0;
10021738e2a2SJames Feist                         sensorFound = true;
10031738e2a2SJames Feist                     }
100450fdfe39SJames Feist                 }
100550fdfe39SJames Feist                 if (!sensorFound)
100650fdfe39SJames Feist                 {
100750fdfe39SJames Feist                     continue;
100850fdfe39SJames Feist                 }
1009*3f0f7bc3SJosh Lehan 
1010*3f0f7bc3SJosh Lehan                 // MissingIsAcceptable same postprocessing as Inputs
1011*3f0f7bc3SJosh Lehan                 for (const std::string& missingAcceptableSensorName :
1012*3f0f7bc3SJosh Lehan                      missingAcceptableSensorNames)
1013*3f0f7bc3SJosh Lehan                 {
1014*3f0f7bc3SJosh Lehan                     std::vector<std::pair<std::string, std::string>>
1015*3f0f7bc3SJosh Lehan                         sensorPathIfacePairs;
1016*3f0f7bc3SJosh Lehan                     if (!findSensors(
1017*3f0f7bc3SJosh Lehan                             sensors,
1018*3f0f7bc3SJosh Lehan                             sensorNameToDbusName(missingAcceptableSensorName),
1019*3f0f7bc3SJosh Lehan                             sensorPathIfacePairs))
1020*3f0f7bc3SJosh Lehan                     {
1021*3f0f7bc3SJosh Lehan                         break;
1022*3f0f7bc3SJosh Lehan                     }
1023*3f0f7bc3SJosh Lehan 
1024*3f0f7bc3SJosh Lehan                     for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
1025*3f0f7bc3SJosh Lehan                     {
1026*3f0f7bc3SJosh Lehan                         std::string shortName =
1027*3f0f7bc3SJosh Lehan                             getSensorNameFromPath(sensorPathIfacePair.first);
1028*3f0f7bc3SJosh Lehan 
1029*3f0f7bc3SJosh Lehan                         missingAcceptableSensors.push_back(shortName);
1030*3f0f7bc3SJosh Lehan                     }
1031*3f0f7bc3SJosh Lehan                 }
1032*3f0f7bc3SJosh Lehan 
10337c6d35d5Sykchiu                 conf::ControllerInfo& info = conf[pidName];
103431058fd3SJosh Lehan 
103531058fd3SJosh Lehan                 std::vector<double> inputTempToMargin;
103631058fd3SJosh Lehan 
103731058fd3SJosh Lehan                 auto findTempToMargin = base.find("TempToMargin");
103831058fd3SJosh Lehan                 if (findTempToMargin != base.end())
103931058fd3SJosh Lehan                 {
104031058fd3SJosh Lehan                     inputTempToMargin =
104131058fd3SJosh Lehan                         std::get<std::vector<double>>(findTempToMargin->second);
104231058fd3SJosh Lehan                 }
104331058fd3SJosh Lehan 
1044*3f0f7bc3SJosh Lehan                 info.inputs = spliceInputs(inputs, inputTempToMargin,
1045*3f0f7bc3SJosh Lehan                                            missingAcceptableSensors);
104650fdfe39SJames Feist 
104722c257abSJames Feist                 info.type = "stepwise";
104822c257abSJames Feist                 info.stepwiseInfo.ts = 1.0; // currently unused
10493dfaafdaSJames Feist                 info.stepwiseInfo.positiveHysteresis = 0.0;
10503dfaafdaSJames Feist                 info.stepwiseInfo.negativeHysteresis = 0.0;
1051608304daSJames Feist                 std::string subtype = std::get<std::string>(base.at("Class"));
1052608304daSJames Feist 
1053608304daSJames Feist                 info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
10543dfaafdaSJames Feist                 auto findPosHyst = base.find("PositiveHysteresis");
10553dfaafdaSJames Feist                 auto findNegHyst = base.find("NegativeHysteresis");
10563dfaafdaSJames Feist                 if (findPosHyst != base.end())
10573dfaafdaSJames Feist                 {
10581f802f5eSJames Feist                     info.stepwiseInfo.positiveHysteresis = std::visit(
1059208abce8SJames Feist                         VariantToDoubleVisitor(), findPosHyst->second);
10603dfaafdaSJames Feist                 }
10613dfaafdaSJames Feist                 if (findNegHyst != base.end())
10623dfaafdaSJames Feist                 {
10635782ab81SJames Feist                     info.stepwiseInfo.negativeHysteresis = std::visit(
1064208abce8SJames Feist                         VariantToDoubleVisitor(), findNegHyst->second);
10653dfaafdaSJames Feist                 }
106622c257abSJames Feist                 std::vector<double> readings =
10671f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Reading"));
106822c257abSJames Feist                 if (readings.size() > ec::maxStepwisePoints)
106922c257abSJames Feist                 {
107022c257abSJames Feist                     throw std::invalid_argument("Too many stepwise points.");
107122c257abSJames Feist                 }
107222c257abSJames Feist                 if (readings.empty())
107322c257abSJames Feist                 {
107422c257abSJames Feist                     throw std::invalid_argument(
107522c257abSJames Feist                         "Must have one stepwise point.");
107622c257abSJames Feist                 }
107722c257abSJames Feist                 std::copy(readings.begin(), readings.end(),
107822c257abSJames Feist                           info.stepwiseInfo.reading);
107922c257abSJames Feist                 if (readings.size() < ec::maxStepwisePoints)
108022c257abSJames Feist                 {
108122c257abSJames Feist                     info.stepwiseInfo.reading[readings.size()] =
10825f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
108322c257abSJames Feist                 }
108422c257abSJames Feist                 std::vector<double> outputs =
10851f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Output"));
108622c257abSJames Feist                 if (readings.size() != outputs.size())
108722c257abSJames Feist                 {
108822c257abSJames Feist                     throw std::invalid_argument(
108922c257abSJames Feist                         "Outputs size must match readings");
109022c257abSJames Feist                 }
109122c257abSJames Feist                 std::copy(outputs.begin(), outputs.end(),
109222c257abSJames Feist                           info.stepwiseInfo.output);
109322c257abSJames Feist                 if (outputs.size() < ec::maxStepwisePoints)
109422c257abSJames Feist                 {
109522c257abSJames Feist                     info.stepwiseInfo.output[outputs.size()] =
10965f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
109722c257abSJames Feist                 }
109822c257abSJames Feist             }
109922c257abSJames Feist         }
110022c257abSJames Feist     }
110139199b4dSPatrick Venture     if constexpr (pid_control::conf::DEBUG)
11027136a5aeSJames Feist     {
110339199b4dSPatrick Venture         debugPrint(sensorConfig, zoneConfig, zoneDetailsConfig);
11047136a5aeSJames Feist     }
1105c959c429SJames Feist     if (zoneConfig.empty() || zoneDetailsConfig.empty())
110650fdfe39SJames Feist     {
11071fe08952SJames Feist         std::cerr
11081fe08952SJames Feist             << "No fan zones, application pausing until new configuration\n";
11091fe08952SJames Feist         return false;
111050fdfe39SJames Feist     }
11111fe08952SJames Feist     return true;
11127136a5aeSJames Feist }
1113a076487aSPatrick Venture 
11147136a5aeSJames Feist } // namespace dbus_configuration
1115a076487aSPatrick Venture } // namespace pid_control
1116