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