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