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 */ 167136a5aeSJames Feist 170771659eSPatrick Venture #include "conf.hpp" 180771659eSPatrick Venture #include "dbus/util.hpp" 190771659eSPatrick Venture 20107a25daSPatrick Venture #include <algorithm> 2164f072a7SJames Feist #include <chrono> 2264f072a7SJames Feist #include <functional> 237136a5aeSJames Feist #include <iostream> 241738e2a2SJames Feist #include <regex> 257136a5aeSJames Feist #include <sdbusplus/bus.hpp> 2664f072a7SJames Feist #include <sdbusplus/bus/match.hpp> 2722c257abSJames Feist #include <sdbusplus/exception.hpp> 287136a5aeSJames Feist #include <set> 2964f072a7SJames Feist #include <thread> 307136a5aeSJames Feist #include <unordered_map> 311f802f5eSJames Feist #include <variant> 327136a5aeSJames Feist 337136a5aeSJames Feist static constexpr bool DEBUG = false; // enable to print found configuration 347136a5aeSJames Feist 35f81f2886SJames Feist extern std::map<std::string, struct conf::SensorConfig> sensorConfig; 36f81f2886SJames Feist extern std::map<int64_t, conf::PIDConf> zoneConfig; 37f81f2886SJames Feist extern std::map<int64_t, struct conf::ZoneConfig> zoneDetailsConfig; 387136a5aeSJames Feist 397136a5aeSJames Feist constexpr const char* pidConfigurationInterface = 407136a5aeSJames Feist "xyz.openbmc_project.Configuration.Pid"; 417136a5aeSJames Feist constexpr const char* objectManagerInterface = 427136a5aeSJames Feist "org.freedesktop.DBus.ObjectManager"; 437136a5aeSJames Feist constexpr const char* pidZoneConfigurationInterface = 447136a5aeSJames Feist "xyz.openbmc_project.Configuration.Pid.Zone"; 4522c257abSJames Feist constexpr const char* stepwiseConfigurationInterface = 4622c257abSJames Feist "xyz.openbmc_project.Configuration.Stepwise"; 47f0096a0cSJames Feist constexpr const char* fanProfileConfigurationIface = 48f0096a0cSJames Feist "xyz.openbmc_project.Configuration.FanProfile"; 49f0096a0cSJames Feist constexpr const char* thermalControlIface = 50f0096a0cSJames Feist "xyz.openbmc_project.Control.ThermalMode"; 517136a5aeSJames Feist constexpr const char* sensorInterface = "xyz.openbmc_project.Sensor.Value"; 527136a5aeSJames Feist constexpr const char* pwmInterface = "xyz.openbmc_project.Control.FanPwm"; 537136a5aeSJames Feist 547136a5aeSJames Feist namespace dbus_configuration 557136a5aeSJames Feist { 567136a5aeSJames Feist 571738e2a2SJames Feist bool findSensors(const std::unordered_map<std::string, std::string>& sensors, 587136a5aeSJames Feist const std::string& search, 591738e2a2SJames Feist std::vector<std::pair<std::string, std::string>>& matches) 607136a5aeSJames Feist { 611738e2a2SJames Feist std::smatch match; 621738e2a2SJames Feist std::regex reg(search); 631738e2a2SJames Feist for (const auto& sensor : sensors) 647136a5aeSJames Feist { 651738e2a2SJames Feist if (std::regex_search(sensor.first, match, reg)) 661738e2a2SJames Feist { 671738e2a2SJames Feist matches.push_back(sensor); 681738e2a2SJames Feist } 697136a5aeSJames Feist } 70107a25daSPatrick Venture 711738e2a2SJames Feist return matches.size() > 0; 727136a5aeSJames Feist } 737136a5aeSJames Feist 747136a5aeSJames Feist // this function prints the configuration into a form similar to the cpp 757136a5aeSJames Feist // generated code to help in verification, should be turned off during normal 767136a5aeSJames Feist // use 777136a5aeSJames Feist void debugPrint(void) 787136a5aeSJames Feist { 797136a5aeSJames Feist // print sensor config 807136a5aeSJames Feist std::cout << "sensor config:\n"; 817136a5aeSJames Feist std::cout << "{\n"; 82c54fbd88SPatrick Venture for (const auto& pair : sensorConfig) 837136a5aeSJames Feist { 847136a5aeSJames Feist 857136a5aeSJames Feist std::cout << "\t{" << pair.first << ",\n\t\t{"; 867136a5aeSJames Feist std::cout << pair.second.type << ", "; 8769c51061SPatrick Venture std::cout << pair.second.readPath << ", "; 8869c51061SPatrick Venture std::cout << pair.second.writePath << ", "; 897136a5aeSJames Feist std::cout << pair.second.min << ", "; 907136a5aeSJames Feist std::cout << pair.second.max << ", "; 917136a5aeSJames Feist std::cout << pair.second.timeout << "},\n\t},\n"; 927136a5aeSJames Feist } 937136a5aeSJames Feist std::cout << "}\n\n"; 947136a5aeSJames Feist std::cout << "ZoneDetailsConfig\n"; 957136a5aeSJames Feist std::cout << "{\n"; 96c54fbd88SPatrick Venture for (const auto& zone : zoneDetailsConfig) 977136a5aeSJames Feist { 987136a5aeSJames Feist std::cout << "\t{" << zone.first << ",\n"; 993484bedaSJames Feist std::cout << "\t\t{" << zone.second.minThermalOutput << ", "; 1008e2fdb34SPatrick Venture std::cout << zone.second.failsafePercent << "}\n\t},\n"; 1017136a5aeSJames Feist } 1027136a5aeSJames Feist std::cout << "}\n\n"; 1037136a5aeSJames Feist std::cout << "ZoneConfig\n"; 1047136a5aeSJames Feist std::cout << "{\n"; 105c54fbd88SPatrick Venture for (const auto& zone : zoneConfig) 1067136a5aeSJames Feist { 1077136a5aeSJames Feist std::cout << "\t{" << zone.first << "\n"; 1084a2dc4d8SPatrick Venture for (const auto& pidconf : zone.second) 1097136a5aeSJames Feist { 1107136a5aeSJames Feist std::cout << "\t\t{" << pidconf.first << ",\n"; 1117136a5aeSJames Feist std::cout << "\t\t\t{" << pidconf.second.type << ",\n"; 1127136a5aeSJames Feist std::cout << "\t\t\t{"; 1134a2dc4d8SPatrick Venture for (const auto& input : pidconf.second.inputs) 1147136a5aeSJames Feist { 1157136a5aeSJames Feist std::cout << "\n\t\t\t" << input << ",\n"; 1167136a5aeSJames Feist } 1177136a5aeSJames Feist std::cout << "\t\t\t}\n"; 1187136a5aeSJames Feist std::cout << "\t\t\t" << pidconf.second.setpoint << ",\n"; 11922c257abSJames Feist std::cout << "\t\t\t{" << pidconf.second.pidInfo.ts << ",\n"; 1207442c37aSPatrick Venture std::cout << "\t\t\t" << pidconf.second.pidInfo.proportionalCoeff 1217442c37aSPatrick Venture << ",\n"; 1227442c37aSPatrick Venture std::cout << "\t\t\t" << pidconf.second.pidInfo.integralCoeff 1237442c37aSPatrick Venture << ",\n"; 1247442c37aSPatrick Venture std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdOffset 1257442c37aSPatrick Venture << ",\n"; 1267442c37aSPatrick Venture std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdGain 1277442c37aSPatrick Venture << ",\n"; 1287442c37aSPatrick Venture std::cout << "\t\t\t{" << pidconf.second.pidInfo.integralLimit.min 1297442c37aSPatrick Venture << "," << pidconf.second.pidInfo.integralLimit.max 1307442c37aSPatrick Venture << "},\n"; 1317442c37aSPatrick Venture std::cout << "\t\t\t{" << pidconf.second.pidInfo.outLim.min << "," 1327442c37aSPatrick Venture << pidconf.second.pidInfo.outLim.max << "},\n"; 1337442c37aSPatrick Venture std::cout << "\t\t\t" << pidconf.second.pidInfo.slewNeg << ",\n"; 1347442c37aSPatrick Venture std::cout << "\t\t\t" << pidconf.second.pidInfo.slewPos << ",\n"; 1357136a5aeSJames Feist std::cout << "\t\t\t}\n\t\t}\n"; 1367136a5aeSJames Feist } 1377136a5aeSJames Feist std::cout << "\t},\n"; 1387136a5aeSJames Feist } 1397136a5aeSJames Feist std::cout << "}\n\n"; 1407136a5aeSJames Feist } 1417136a5aeSJames Feist 14250fdfe39SJames Feist int eventHandler(sd_bus_message*, void*, sd_bus_error*) 14350fdfe39SJames Feist { 14450fdfe39SJames Feist // do a brief sleep as we tend to get a bunch of these events at 14550fdfe39SJames Feist // once 14650fdfe39SJames Feist std::this_thread::sleep_for(std::chrono::seconds(5)); 14750fdfe39SJames Feist std::cout << "New configuration detected, restarting\n."; 14850fdfe39SJames Feist std::exit(EXIT_SUCCESS); // service file should make us restart 14950fdfe39SJames Feist return 1; 15050fdfe39SJames Feist } 15150fdfe39SJames Feist 152ffd418bbSJames Feist size_t getZoneIndex(const std::string& name, std::vector<std::string>& zones) 153ffd418bbSJames Feist { 154ffd418bbSJames Feist auto it = std::find(zones.begin(), zones.end(), name); 155ffd418bbSJames Feist if (it == zones.end()) 156ffd418bbSJames Feist { 157ffd418bbSJames Feist zones.emplace_back(name); 158ffd418bbSJames Feist it = zones.end() - 1; 159ffd418bbSJames Feist } 160ffd418bbSJames Feist 161ffd418bbSJames Feist return it - zones.begin(); 162ffd418bbSJames Feist } 163ffd418bbSJames Feist 164f0096a0cSJames Feist std::vector<std::string> getSelectedProfiles(sdbusplus::bus::bus& bus) 165f0096a0cSJames Feist { 166f0096a0cSJames Feist std::vector<std::string> ret; 167f0096a0cSJames Feist auto mapper = 168f0096a0cSJames Feist bus.new_method_call("xyz.openbmc_project.ObjectMapper", 169f0096a0cSJames Feist "/xyz/openbmc_project/object_mapper", 170f0096a0cSJames Feist "xyz.openbmc_project.ObjectMapper", "GetSubTree"); 171f0096a0cSJames Feist mapper.append("/", 0, std::array<const char*, 1>{thermalControlIface}); 172f0096a0cSJames Feist std::unordered_map< 173f0096a0cSJames Feist std::string, std::unordered_map<std::string, std::vector<std::string>>> 174f0096a0cSJames Feist respData; 175f0096a0cSJames Feist 176f0096a0cSJames Feist try 177f0096a0cSJames Feist { 178f0096a0cSJames Feist auto resp = bus.call(mapper); 179f0096a0cSJames Feist resp.read(respData); 180f0096a0cSJames Feist } 181f0096a0cSJames Feist catch (sdbusplus::exception_t&) 182f0096a0cSJames Feist { 183f0096a0cSJames Feist // can't do anything without mapper call data 184f0096a0cSJames Feist throw std::runtime_error("ObjectMapper Call Failure"); 185f0096a0cSJames Feist } 186f0096a0cSJames Feist if (respData.empty()) 187f0096a0cSJames Feist { 188f0096a0cSJames Feist // if the user has profiles but doesn't expose the interface to select 189f0096a0cSJames Feist // one, just go ahead without using profiles 190f0096a0cSJames Feist return ret; 191f0096a0cSJames Feist } 192f0096a0cSJames Feist 193f0096a0cSJames Feist // assumption is that we should only have a small handful of selected 194f0096a0cSJames Feist // profiles at a time (probably only 1), so calling each individually should 195f0096a0cSJames Feist // not incur a large cost 196f0096a0cSJames Feist for (const auto& objectPair : respData) 197f0096a0cSJames Feist { 198f0096a0cSJames Feist const std::string& path = objectPair.first; 199f0096a0cSJames Feist for (const auto& ownerPair : objectPair.second) 200f0096a0cSJames Feist { 201f0096a0cSJames Feist const std::string& busName = ownerPair.first; 202f0096a0cSJames Feist auto getProfile = 203f0096a0cSJames Feist bus.new_method_call(busName.c_str(), path.c_str(), 204f0096a0cSJames Feist "org.freedesktop.DBus.Properties", "Get"); 205f0096a0cSJames Feist getProfile.append(thermalControlIface, "Current"); 206f0096a0cSJames Feist std::variant<std::string> variantResp; 207f0096a0cSJames Feist try 208f0096a0cSJames Feist { 209f0096a0cSJames Feist auto resp = bus.call(getProfile); 210f0096a0cSJames Feist resp.read(variantResp); 211f0096a0cSJames Feist } 212f0096a0cSJames Feist catch (sdbusplus::exception_t&) 213f0096a0cSJames Feist { 214f0096a0cSJames Feist throw std::runtime_error("Failure getting profile"); 215f0096a0cSJames Feist } 216f0096a0cSJames Feist std::string mode = std::get<std::string>(variantResp); 217f0096a0cSJames Feist ret.emplace_back(std::move(mode)); 218f0096a0cSJames Feist } 219f0096a0cSJames Feist } 220f0096a0cSJames Feist if constexpr (DEBUG) 221f0096a0cSJames Feist { 222f0096a0cSJames Feist std::cout << "Profiles selected: "; 223f0096a0cSJames Feist for (const auto& profile : ret) 224f0096a0cSJames Feist { 225f0096a0cSJames Feist std::cout << profile << " "; 226f0096a0cSJames Feist } 227f0096a0cSJames Feist std::cout << "\n"; 228f0096a0cSJames Feist } 229f0096a0cSJames Feist return ret; 230f0096a0cSJames Feist } 231f0096a0cSJames Feist 2327136a5aeSJames Feist void init(sdbusplus::bus::bus& bus) 2337136a5aeSJames Feist { 23422c257abSJames Feist using DbusVariantType = 2351f802f5eSJames Feist std::variant<uint64_t, int64_t, double, std::string, 2361f802f5eSJames Feist std::vector<std::string>, std::vector<double>>; 23722c257abSJames Feist 2387136a5aeSJames Feist using ManagedObjectType = std::unordered_map< 2397136a5aeSJames Feist sdbusplus::message::object_path, 2407136a5aeSJames Feist std::unordered_map<std::string, 24122c257abSJames Feist std::unordered_map<std::string, DbusVariantType>>>; 24264f072a7SJames Feist 24350fdfe39SJames Feist // restart on configuration properties changed 24450fdfe39SJames Feist static sdbusplus::bus::match::match configMatch( 24564f072a7SJames Feist bus, 24664f072a7SJames Feist "type='signal',member='PropertiesChanged',arg0namespace='" + 24764f072a7SJames Feist std::string(pidConfigurationInterface) + "'", 24864f072a7SJames Feist eventHandler); 24964f072a7SJames Feist 250f0096a0cSJames Feist // restart on profile change 251f0096a0cSJames Feist static sdbusplus::bus::match::match profileMatch( 252f0096a0cSJames Feist bus, 253f0096a0cSJames Feist "type='signal',member='PropertiesChanged',arg0namespace='" + 254f0096a0cSJames Feist std::string(thermalControlIface) + "'", 255f0096a0cSJames Feist eventHandler); 256f0096a0cSJames Feist 25750fdfe39SJames Feist // restart on sensors changed 25850fdfe39SJames Feist static sdbusplus::bus::match::match sensorAdded( 25950fdfe39SJames Feist bus, 26050fdfe39SJames Feist "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/" 26150fdfe39SJames Feist "sensors/'", 26250fdfe39SJames Feist eventHandler); 26350fdfe39SJames Feist 2647136a5aeSJames Feist auto mapper = 2657136a5aeSJames Feist bus.new_method_call("xyz.openbmc_project.ObjectMapper", 2667136a5aeSJames Feist "/xyz/openbmc_project/object_mapper", 2677136a5aeSJames Feist "xyz.openbmc_project.ObjectMapper", "GetSubTree"); 26826e8c6a9SJames Feist mapper.append("/", 0, 269f0096a0cSJames Feist std::array<const char*, 7>{ 270f0096a0cSJames Feist objectManagerInterface, pidConfigurationInterface, 2717136a5aeSJames Feist pidZoneConfigurationInterface, 272f0096a0cSJames Feist stepwiseConfigurationInterface, sensorInterface, 273f0096a0cSJames Feist pwmInterface, fanProfileConfigurationIface}); 27422c257abSJames Feist std::unordered_map< 27522c257abSJames Feist std::string, std::unordered_map<std::string, std::vector<std::string>>> 27622c257abSJames Feist respData; 27722c257abSJames Feist try 27822c257abSJames Feist { 2797136a5aeSJames Feist auto resp = bus.call(mapper); 2807136a5aeSJames Feist resp.read(respData); 28122c257abSJames Feist } 28222c257abSJames Feist catch (sdbusplus::exception_t&) 28322c257abSJames Feist { 28422c257abSJames Feist // can't do anything without mapper call data 28522c257abSJames Feist throw std::runtime_error("ObjectMapper Call Failure"); 28622c257abSJames Feist } 28722c257abSJames Feist 2887136a5aeSJames Feist if (respData.empty()) 2897136a5aeSJames Feist { 29022c257abSJames Feist // can't do anything without mapper call data 2917136a5aeSJames Feist throw std::runtime_error("No configuration data available from Mapper"); 2927136a5aeSJames Feist } 2937136a5aeSJames Feist // create a map of pair of <has pid configuration, ObjectManager path> 2947136a5aeSJames Feist std::unordered_map<std::string, std::pair<bool, std::string>> owners; 2957136a5aeSJames Feist // and a map of <path, interface> for sensors 2967136a5aeSJames Feist std::unordered_map<std::string, std::string> sensors; 2977136a5aeSJames Feist for (const auto& objectPair : respData) 2987136a5aeSJames Feist { 2997136a5aeSJames Feist for (const auto& ownerPair : objectPair.second) 3007136a5aeSJames Feist { 3017136a5aeSJames Feist auto& owner = owners[ownerPair.first]; 3027136a5aeSJames Feist for (const std::string& interface : ownerPair.second) 3037136a5aeSJames Feist { 3047136a5aeSJames Feist 3057136a5aeSJames Feist if (interface == objectManagerInterface) 3067136a5aeSJames Feist { 3077136a5aeSJames Feist owner.second = objectPair.first; 3087136a5aeSJames Feist } 3097136a5aeSJames Feist if (interface == pidConfigurationInterface || 31022c257abSJames Feist interface == pidZoneConfigurationInterface || 31122c257abSJames Feist interface == stepwiseConfigurationInterface) 3127136a5aeSJames Feist { 3137136a5aeSJames Feist owner.first = true; 3147136a5aeSJames Feist } 3157136a5aeSJames Feist if (interface == sensorInterface || interface == pwmInterface) 3167136a5aeSJames Feist { 3177136a5aeSJames Feist // we're not interested in pwm sensors, just pwm control 3187136a5aeSJames Feist if (interface == sensorInterface && 3197136a5aeSJames Feist objectPair.first.find("pwm") != std::string::npos) 3207136a5aeSJames Feist { 3217136a5aeSJames Feist continue; 3227136a5aeSJames Feist } 3237136a5aeSJames Feist sensors[objectPair.first] = interface; 3247136a5aeSJames Feist } 3257136a5aeSJames Feist } 3267136a5aeSJames Feist } 3277136a5aeSJames Feist } 3287136a5aeSJames Feist ManagedObjectType configurations; 329f0096a0cSJames Feist ManagedObjectType profiles; 3307136a5aeSJames Feist for (const auto& owner : owners) 3317136a5aeSJames Feist { 3327136a5aeSJames Feist // skip if no pid configuration (means probably a sensor) 3337136a5aeSJames Feist if (!owner.second.first) 3347136a5aeSJames Feist { 3357136a5aeSJames Feist continue; 3367136a5aeSJames Feist } 3377136a5aeSJames Feist auto endpoint = bus.new_method_call( 3387136a5aeSJames Feist owner.first.c_str(), owner.second.second.c_str(), 3397136a5aeSJames Feist "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 34022c257abSJames Feist ManagedObjectType configuration; 34122c257abSJames Feist try 34222c257abSJames Feist { 3437136a5aeSJames Feist auto responce = bus.call(endpoint); 3447136a5aeSJames Feist responce.read(configuration); 34522c257abSJames Feist } 34622c257abSJames Feist catch (sdbusplus::exception_t&) 34722c257abSJames Feist { 34822c257abSJames Feist // this shouldn't happen, probably means daemon crashed 34922c257abSJames Feist throw std::runtime_error("Error getting managed objects from " + 35022c257abSJames Feist owner.first); 35122c257abSJames Feist } 35222c257abSJames Feist 3537136a5aeSJames Feist for (auto& pathPair : configuration) 3547136a5aeSJames Feist { 3557136a5aeSJames Feist if (pathPair.second.find(pidConfigurationInterface) != 3567136a5aeSJames Feist pathPair.second.end() || 3577136a5aeSJames Feist pathPair.second.find(pidZoneConfigurationInterface) != 35822c257abSJames Feist pathPair.second.end() || 35922c257abSJames Feist pathPair.second.find(stepwiseConfigurationInterface) != 3607136a5aeSJames Feist pathPair.second.end()) 3617136a5aeSJames Feist { 3627136a5aeSJames Feist configurations.emplace(pathPair); 3637136a5aeSJames Feist } 364f0096a0cSJames Feist if (pathPair.second.find(fanProfileConfigurationIface) != 365f0096a0cSJames Feist pathPair.second.end()) 366f0096a0cSJames Feist { 367f0096a0cSJames Feist profiles.emplace(pathPair); 368f0096a0cSJames Feist } 369f0096a0cSJames Feist } 370f0096a0cSJames Feist } 371f0096a0cSJames Feist 372f0096a0cSJames Feist // remove controllers from config that aren't in the current profile(s) 373f0096a0cSJames Feist if (profiles.size()) 374f0096a0cSJames Feist { 375f0096a0cSJames Feist std::vector<std::string> selectedProfiles = getSelectedProfiles(bus); 376f0096a0cSJames Feist if (selectedProfiles.size()) 377f0096a0cSJames Feist { 378f0096a0cSJames Feist // make the names match the dbus name 379f0096a0cSJames Feist for (auto& profile : selectedProfiles) 380f0096a0cSJames Feist { 381f0096a0cSJames Feist std::replace(profile.begin(), profile.end(), ' ', '_'); 382f0096a0cSJames Feist } 383f0096a0cSJames Feist 384f0096a0cSJames Feist // remove profiles that aren't supported 385f0096a0cSJames Feist for (auto it = profiles.begin(); it != profiles.end();) 386f0096a0cSJames Feist { 387f0096a0cSJames Feist auto& path = it->first.str; 388f0096a0cSJames Feist auto inConfig = std::find_if( 389f0096a0cSJames Feist selectedProfiles.begin(), selectedProfiles.end(), 390f0096a0cSJames Feist [&path](const std::string& key) { 391f0096a0cSJames Feist return (path.find(key) != std::string::npos); 392f0096a0cSJames Feist }); 393f0096a0cSJames Feist if (inConfig == selectedProfiles.end()) 394f0096a0cSJames Feist { 395f0096a0cSJames Feist it = profiles.erase(it); 396f0096a0cSJames Feist } 397f0096a0cSJames Feist else 398f0096a0cSJames Feist { 399f0096a0cSJames Feist it++; 400f0096a0cSJames Feist } 401f0096a0cSJames Feist } 402f0096a0cSJames Feist std::vector<std::string> allowedControllers; 403f0096a0cSJames Feist 404f0096a0cSJames Feist // create a vector of profile match strings 405f0096a0cSJames Feist for (const auto& profile : profiles) 406f0096a0cSJames Feist { 407f0096a0cSJames Feist const auto& interface = 408f0096a0cSJames Feist profile.second.at(fanProfileConfigurationIface); 409f0096a0cSJames Feist auto findController = interface.find("Controllers"); 410f0096a0cSJames Feist if (findController == interface.end()) 411f0096a0cSJames Feist { 412f0096a0cSJames Feist throw std::runtime_error("Profile Missing Controllers"); 413f0096a0cSJames Feist } 414f0096a0cSJames Feist std::vector<std::string> controllers = 415f0096a0cSJames Feist std::get<std::vector<std::string>>(findController->second); 416f0096a0cSJames Feist allowedControllers.insert(allowedControllers.end(), 417f0096a0cSJames Feist controllers.begin(), 418f0096a0cSJames Feist controllers.end()); 419f0096a0cSJames Feist } 420f0096a0cSJames Feist std::vector<std::regex> regexes; 421f0096a0cSJames Feist for (auto& controller : allowedControllers) 422f0096a0cSJames Feist { 423f0096a0cSJames Feist std::replace(controller.begin(), controller.end(), ' ', '_'); 424f0096a0cSJames Feist try 425f0096a0cSJames Feist { 426f0096a0cSJames Feist regexes.push_back(std::regex(controller)); 427f0096a0cSJames Feist } 428f0096a0cSJames Feist catch (std::regex_error&) 429f0096a0cSJames Feist { 430f0096a0cSJames Feist std::cerr << "Invalid regex: " << controller << "\n"; 431f0096a0cSJames Feist throw; 432f0096a0cSJames Feist } 433f0096a0cSJames Feist } 434f0096a0cSJames Feist 435f0096a0cSJames Feist // remove configurations that don't match any of the regexes 436f0096a0cSJames Feist for (auto it = configurations.begin(); it != configurations.end();) 437f0096a0cSJames Feist { 438f0096a0cSJames Feist const std::string& path = it->first; 439f0096a0cSJames Feist size_t lastSlash = path.rfind("/"); 440f0096a0cSJames Feist if (lastSlash == std::string::npos) 441f0096a0cSJames Feist { 442f0096a0cSJames Feist // if this happens, the mapper has a bug 443f0096a0cSJames Feist throw std::runtime_error("Invalid path in configuration"); 444f0096a0cSJames Feist } 445f0096a0cSJames Feist std::string name = path.substr(lastSlash); 446f0096a0cSJames Feist auto allowed = std::find_if( 447f0096a0cSJames Feist regexes.begin(), regexes.end(), [&name](auto& reg) { 448f0096a0cSJames Feist std::smatch match; 449f0096a0cSJames Feist return std::regex_search(name, match, reg); 450f0096a0cSJames Feist }); 451f0096a0cSJames Feist if (allowed == regexes.end()) 452f0096a0cSJames Feist { 453f0096a0cSJames Feist auto findZone = 454f0096a0cSJames Feist it->second.find(pidZoneConfigurationInterface); 455f0096a0cSJames Feist 456f0096a0cSJames Feist // if there is a fanzone under the given configuration, keep 457f0096a0cSJames Feist // it but remove any of the other controllers 458f0096a0cSJames Feist if (findZone != it->second.end()) 459f0096a0cSJames Feist { 460f0096a0cSJames Feist for (auto subIt = it->second.begin(); 461f0096a0cSJames Feist subIt != it->second.end();) 462f0096a0cSJames Feist { 463f0096a0cSJames Feist if (subIt == findZone) 464f0096a0cSJames Feist { 465f0096a0cSJames Feist subIt++; 466f0096a0cSJames Feist } 467f0096a0cSJames Feist else 468f0096a0cSJames Feist { 469f0096a0cSJames Feist subIt = it->second.erase(subIt); 470f0096a0cSJames Feist } 471f0096a0cSJames Feist } 472f0096a0cSJames Feist it++; 473f0096a0cSJames Feist } 474f0096a0cSJames Feist else 475f0096a0cSJames Feist { 476f0096a0cSJames Feist it = configurations.erase(it); 477f0096a0cSJames Feist } 478f0096a0cSJames Feist } 479f0096a0cSJames Feist else 480f0096a0cSJames Feist { 481f0096a0cSJames Feist it++; 482f0096a0cSJames Feist } 483f0096a0cSJames Feist } 4847136a5aeSJames Feist } 4857136a5aeSJames Feist } 4868c3c51eeSJames Feist 4878c3c51eeSJames Feist // on dbus having an index field is a bit strange, so randomly 4888c3c51eeSJames Feist // assign index based on name property 489ffd418bbSJames Feist std::vector<std::string> foundZones; 4907136a5aeSJames Feist for (const auto& configuration : configurations) 4917136a5aeSJames Feist { 4927136a5aeSJames Feist auto findZone = 4937136a5aeSJames Feist configuration.second.find(pidZoneConfigurationInterface); 4947136a5aeSJames Feist if (findZone != configuration.second.end()) 4957136a5aeSJames Feist { 4967136a5aeSJames Feist const auto& zone = findZone->second; 497ffd418bbSJames Feist 4981f802f5eSJames Feist const std::string& name = std::get<std::string>(zone.at("Name")); 499ffd418bbSJames Feist size_t index = getZoneIndex(name, foundZones); 5008c3c51eeSJames Feist 501c54fbd88SPatrick Venture auto& details = zoneDetailsConfig[index]; 5023484bedaSJames Feist details.minThermalOutput = std::visit(VariantToDoubleVisitor(), 5033484bedaSJames Feist zone.at("MinThermalOutput")); 5048e2fdb34SPatrick Venture details.failsafePercent = std::visit(VariantToDoubleVisitor(), 5051f802f5eSJames Feist zone.at("FailSafePercent")); 5067136a5aeSJames Feist } 5077136a5aeSJames Feist auto findBase = configuration.second.find(pidConfigurationInterface); 50822c257abSJames Feist if (findBase != configuration.second.end()) 5097136a5aeSJames Feist { 5108c3c51eeSJames Feist 51122c257abSJames Feist const auto& base = 51222c257abSJames Feist configuration.second.at(pidConfigurationInterface); 5138c3c51eeSJames Feist const std::vector<std::string>& zones = 5141f802f5eSJames Feist std::get<std::vector<std::string>>(base.at("Zones")); 5158c3c51eeSJames Feist for (const std::string& zone : zones) 5168c3c51eeSJames Feist { 517ffd418bbSJames Feist size_t index = getZoneIndex(zone, foundZones); 518f81f2886SJames Feist conf::PIDConf& conf = zoneConfig[index]; 51950fdfe39SJames Feist 52050fdfe39SJames Feist std::vector<std::string> sensorNames = 5211f802f5eSJames Feist std::get<std::vector<std::string>>(base.at("Inputs")); 52250fdfe39SJames Feist auto findOutputs = 52350fdfe39SJames Feist base.find("Outputs"); // currently only fans have outputs 52450fdfe39SJames Feist if (findOutputs != base.end()) 52550fdfe39SJames Feist { 52650fdfe39SJames Feist std::vector<std::string> outputs = 5271f802f5eSJames Feist std::get<std::vector<std::string>>(findOutputs->second); 52850fdfe39SJames Feist sensorNames.insert(sensorNames.end(), outputs.begin(), 52950fdfe39SJames Feist outputs.end()); 53050fdfe39SJames Feist } 5311738e2a2SJames Feist 53250fdfe39SJames Feist std::vector<std::string> inputs; 5331738e2a2SJames Feist std::vector<std::pair<std::string, std::string>> 5341738e2a2SJames Feist sensorInterfaces; 53550fdfe39SJames Feist for (const std::string& sensorName : sensorNames) 53650fdfe39SJames Feist { 53750fdfe39SJames Feist std::string name = sensorName; 53850fdfe39SJames Feist // replace spaces with underscores to be legal on dbus 53950fdfe39SJames Feist std::replace(name.begin(), name.end(), ' ', '_'); 5401738e2a2SJames Feist findSensors(sensors, name, sensorInterfaces); 54150fdfe39SJames Feist } 5421738e2a2SJames Feist 5431738e2a2SJames Feist // if the sensors aren't available in the current state, don't 5441738e2a2SJames Feist // add them to the configuration. 5451738e2a2SJames Feist if (sensorInterfaces.empty()) 5461738e2a2SJames Feist { 5471738e2a2SJames Feist continue; 5481738e2a2SJames Feist } 5491738e2a2SJames Feist for (const auto& sensorPathIfacePair : sensorInterfaces) 5501738e2a2SJames Feist { 5511738e2a2SJames Feist 55250fdfe39SJames Feist if (sensorPathIfacePair.second == sensorInterface) 55350fdfe39SJames Feist { 5541738e2a2SJames Feist size_t idx = 5551738e2a2SJames Feist sensorPathIfacePair.first.find_last_of("/") + 1; 5561738e2a2SJames Feist std::string shortName = 5571738e2a2SJames Feist sensorPathIfacePair.first.substr(idx); 5581738e2a2SJames Feist 5591738e2a2SJames Feist inputs.push_back(shortName); 5601738e2a2SJames Feist auto& config = sensorConfig[shortName]; 5611f802f5eSJames Feist config.type = std::get<std::string>(base.at("Class")); 56269c51061SPatrick Venture config.readPath = sensorPathIfacePair.first; 56350fdfe39SJames Feist // todo: maybe un-hardcode this if we run into slower 56450fdfe39SJames Feist // timeouts with sensors 56550fdfe39SJames Feist if (config.type == "temp") 56650fdfe39SJames Feist { 5672642cb54SJames Feist config.timeout = 0; 56850fdfe39SJames Feist } 56975eb769dSJames Feist else if (config.type == "fan") 57075eb769dSJames Feist { 57175eb769dSJames Feist config.max = conf::inheritValueFromDbus; 57275eb769dSJames Feist config.min = conf::inheritValueFromDbus; 57375eb769dSJames Feist } 57450fdfe39SJames Feist } 57550fdfe39SJames Feist else if (sensorPathIfacePair.second == pwmInterface) 57650fdfe39SJames Feist { 57750fdfe39SJames Feist // copy so we can modify it 57850fdfe39SJames Feist for (std::string otherSensor : sensorNames) 57950fdfe39SJames Feist { 5801738e2a2SJames Feist std::replace(otherSensor.begin(), otherSensor.end(), 5811738e2a2SJames Feist ' ', '_'); 5821738e2a2SJames Feist if (sensorPathIfacePair.first.find(otherSensor) != 5831738e2a2SJames Feist std::string::npos) 58450fdfe39SJames Feist { 58550fdfe39SJames Feist continue; 58650fdfe39SJames Feist } 5871738e2a2SJames Feist 588c54fbd88SPatrick Venture auto& config = sensorConfig[otherSensor]; 58969c51061SPatrick Venture config.writePath = sensorPathIfacePair.first; 59050fdfe39SJames Feist // todo: un-hardcode this if there are fans with 59150fdfe39SJames Feist // different ranges 59250fdfe39SJames Feist config.max = 255; 59350fdfe39SJames Feist config.min = 0; 59450fdfe39SJames Feist } 59550fdfe39SJames Feist } 59650fdfe39SJames Feist } 5971738e2a2SJames Feist 598f81f2886SJames Feist struct conf::ControllerInfo& info = 5991f802f5eSJames Feist conf[std::get<std::string>(base.at("Name"))]; 60050fdfe39SJames Feist info.inputs = std::move(inputs); 60150fdfe39SJames Feist 6021f802f5eSJames Feist info.type = std::get<std::string>(base.at("Class")); 6038c3c51eeSJames Feist // todo: auto generation yaml -> c script seems to discard this 6048c3c51eeSJames Feist // value for fans, verify this is okay 6057136a5aeSJames Feist if (info.type == "fan") 6067136a5aeSJames Feist { 6077136a5aeSJames Feist info.setpoint = 0; 6087136a5aeSJames Feist } 6097136a5aeSJames Feist else 6107136a5aeSJames Feist { 6111f802f5eSJames Feist info.setpoint = std::visit(VariantToDoubleVisitor(), 612208abce8SJames Feist base.at("SetPoint")); 6137136a5aeSJames Feist } 61422c257abSJames Feist info.pidInfo.ts = 1.0; // currently unused 6157442c37aSPatrick Venture info.pidInfo.proportionalCoeff = std::visit( 6167442c37aSPatrick Venture VariantToDoubleVisitor(), base.at("PCoefficient")); 6177442c37aSPatrick Venture info.pidInfo.integralCoeff = std::visit( 6187442c37aSPatrick Venture VariantToDoubleVisitor(), base.at("ICoefficient")); 6197442c37aSPatrick Venture info.pidInfo.feedFwdOffset = std::visit( 6207442c37aSPatrick Venture VariantToDoubleVisitor(), base.at("FFOffCoefficient")); 6217442c37aSPatrick Venture info.pidInfo.feedFwdGain = std::visit( 6227442c37aSPatrick Venture VariantToDoubleVisitor(), base.at("FFGainCoefficient")); 6237442c37aSPatrick Venture info.pidInfo.integralLimit.max = 6241f802f5eSJames Feist std::visit(VariantToDoubleVisitor(), base.at("ILimitMax")); 6257442c37aSPatrick Venture info.pidInfo.integralLimit.min = 6261f802f5eSJames Feist std::visit(VariantToDoubleVisitor(), base.at("ILimitMin")); 6277442c37aSPatrick Venture info.pidInfo.outLim.max = std::visit(VariantToDoubleVisitor(), 6281f802f5eSJames Feist base.at("OutLimitMax")); 6297442c37aSPatrick Venture info.pidInfo.outLim.min = std::visit(VariantToDoubleVisitor(), 6301f802f5eSJames Feist base.at("OutLimitMin")); 6317442c37aSPatrick Venture info.pidInfo.slewNeg = 6321f802f5eSJames Feist std::visit(VariantToDoubleVisitor(), base.at("SlewNeg")); 6337442c37aSPatrick Venture info.pidInfo.slewPos = 6341f802f5eSJames Feist std::visit(VariantToDoubleVisitor(), base.at("SlewPos")); 635572c43daSJames Feist double negativeHysteresis = 0; 636572c43daSJames Feist double positiveHysteresis = 0; 637572c43daSJames Feist 638572c43daSJames Feist auto findNeg = base.find("NegativeHysteresis"); 639572c43daSJames Feist auto findPos = base.find("PositiveHysteresis"); 640572c43daSJames Feist if (findNeg != base.end()) 641572c43daSJames Feist { 6421f802f5eSJames Feist negativeHysteresis = 6431f802f5eSJames Feist std::visit(VariantToDoubleVisitor(), findNeg->second); 644572c43daSJames Feist } 645572c43daSJames Feist 646572c43daSJames Feist if (findPos != base.end()) 647572c43daSJames Feist { 6481f802f5eSJames Feist positiveHysteresis = 6491f802f5eSJames Feist std::visit(VariantToDoubleVisitor(), findPos->second); 650572c43daSJames Feist } 651572c43daSJames Feist info.pidInfo.negativeHysteresis = negativeHysteresis; 652572c43daSJames Feist info.pidInfo.positiveHysteresis = positiveHysteresis; 6537136a5aeSJames Feist } 6548c3c51eeSJames Feist } 65522c257abSJames Feist auto findStepwise = 65622c257abSJames Feist configuration.second.find(stepwiseConfigurationInterface); 65722c257abSJames Feist if (findStepwise != configuration.second.end()) 65822c257abSJames Feist { 65922c257abSJames Feist const auto& base = findStepwise->second; 66022c257abSJames Feist const std::vector<std::string>& zones = 6611f802f5eSJames Feist std::get<std::vector<std::string>>(base.at("Zones")); 66222c257abSJames Feist for (const std::string& zone : zones) 66322c257abSJames Feist { 664ffd418bbSJames Feist size_t index = getZoneIndex(zone, foundZones); 665f81f2886SJames Feist conf::PIDConf& conf = zoneConfig[index]; 66650fdfe39SJames Feist 66750fdfe39SJames Feist std::vector<std::string> inputs; 66850fdfe39SJames Feist std::vector<std::string> sensorNames = 6691f802f5eSJames Feist std::get<std::vector<std::string>>(base.at("Inputs")); 67050fdfe39SJames Feist 6711738e2a2SJames Feist bool sensorFound = false; 67250fdfe39SJames Feist for (const std::string& sensorName : sensorNames) 67350fdfe39SJames Feist { 67450fdfe39SJames Feist std::string name = sensorName; 67550fdfe39SJames Feist // replace spaces with underscores to be legal on dbus 67650fdfe39SJames Feist std::replace(name.begin(), name.end(), ' ', '_'); 6771738e2a2SJames Feist std::vector<std::pair<std::string, std::string>> 6781738e2a2SJames Feist sensorPathIfacePairs; 67950fdfe39SJames Feist 6801738e2a2SJames Feist if (!findSensors(sensors, name, sensorPathIfacePairs)) 68150fdfe39SJames Feist { 68250fdfe39SJames Feist break; 68350fdfe39SJames Feist } 68450fdfe39SJames Feist 6851738e2a2SJames Feist for (const auto& sensorPathIfacePair : sensorPathIfacePairs) 6861738e2a2SJames Feist { 6871738e2a2SJames Feist size_t idx = 6881738e2a2SJames Feist sensorPathIfacePair.first.find_last_of("/") + 1; 6891738e2a2SJames Feist std::string shortName = 6901738e2a2SJames Feist sensorPathIfacePair.first.substr(idx); 6911738e2a2SJames Feist 6921738e2a2SJames Feist inputs.push_back(shortName); 6931738e2a2SJames Feist auto& config = sensorConfig[shortName]; 69469c51061SPatrick Venture config.readPath = sensorPathIfacePair.first; 69550fdfe39SJames Feist config.type = "temp"; 69650fdfe39SJames Feist // todo: maybe un-hardcode this if we run into slower 69750fdfe39SJames Feist // timeouts with sensors 69850fdfe39SJames Feist 6992642cb54SJames Feist config.timeout = 0; 7001738e2a2SJames Feist sensorFound = true; 7011738e2a2SJames Feist } 70250fdfe39SJames Feist } 70350fdfe39SJames Feist if (!sensorFound) 70450fdfe39SJames Feist { 70550fdfe39SJames Feist continue; 70650fdfe39SJames Feist } 707f81f2886SJames Feist struct conf::ControllerInfo& info = 7081f802f5eSJames Feist conf[std::get<std::string>(base.at("Name"))]; 70950fdfe39SJames Feist info.inputs = std::move(inputs); 71050fdfe39SJames Feist 71122c257abSJames Feist info.type = "stepwise"; 71222c257abSJames Feist info.stepwiseInfo.ts = 1.0; // currently unused 7133dfaafdaSJames Feist info.stepwiseInfo.positiveHysteresis = 0.0; 7143dfaafdaSJames Feist info.stepwiseInfo.negativeHysteresis = 0.0; 715608304daSJames Feist std::string subtype = std::get<std::string>(base.at("Class")); 716608304daSJames Feist 717608304daSJames Feist info.stepwiseInfo.isCeiling = (subtype == "Ceiling"); 7183dfaafdaSJames Feist auto findPosHyst = base.find("PositiveHysteresis"); 7193dfaafdaSJames Feist auto findNegHyst = base.find("NegativeHysteresis"); 7203dfaafdaSJames Feist if (findPosHyst != base.end()) 7213dfaafdaSJames Feist { 7221f802f5eSJames Feist info.stepwiseInfo.positiveHysteresis = std::visit( 723208abce8SJames Feist VariantToDoubleVisitor(), findPosHyst->second); 7243dfaafdaSJames Feist } 7253dfaafdaSJames Feist if (findNegHyst != base.end()) 7263dfaafdaSJames Feist { 727*5782ab81SJames Feist info.stepwiseInfo.negativeHysteresis = std::visit( 728208abce8SJames Feist VariantToDoubleVisitor(), findNegHyst->second); 7293dfaafdaSJames Feist } 73022c257abSJames Feist std::vector<double> readings = 7311f802f5eSJames Feist std::get<std::vector<double>>(base.at("Reading")); 73222c257abSJames Feist if (readings.size() > ec::maxStepwisePoints) 73322c257abSJames Feist { 73422c257abSJames Feist throw std::invalid_argument("Too many stepwise points."); 73522c257abSJames Feist } 73622c257abSJames Feist if (readings.empty()) 73722c257abSJames Feist { 73822c257abSJames Feist throw std::invalid_argument( 73922c257abSJames Feist "Must have one stepwise point."); 74022c257abSJames Feist } 74122c257abSJames Feist std::copy(readings.begin(), readings.end(), 74222c257abSJames Feist info.stepwiseInfo.reading); 74322c257abSJames Feist if (readings.size() < ec::maxStepwisePoints) 74422c257abSJames Feist { 74522c257abSJames Feist info.stepwiseInfo.reading[readings.size()] = 7465f59c0fdSPatrick Venture std::numeric_limits<double>::quiet_NaN(); 74722c257abSJames Feist } 74822c257abSJames Feist std::vector<double> outputs = 7491f802f5eSJames Feist std::get<std::vector<double>>(base.at("Output")); 75022c257abSJames Feist if (readings.size() != outputs.size()) 75122c257abSJames Feist { 75222c257abSJames Feist throw std::invalid_argument( 75322c257abSJames Feist "Outputs size must match readings"); 75422c257abSJames Feist } 75522c257abSJames Feist std::copy(outputs.begin(), outputs.end(), 75622c257abSJames Feist info.stepwiseInfo.output); 75722c257abSJames Feist if (outputs.size() < ec::maxStepwisePoints) 75822c257abSJames Feist { 75922c257abSJames Feist info.stepwiseInfo.output[outputs.size()] = 7605f59c0fdSPatrick Venture std::numeric_limits<double>::quiet_NaN(); 76122c257abSJames Feist } 76222c257abSJames Feist } 76322c257abSJames Feist } 76422c257abSJames Feist } 765f0096a0cSJames Feist if constexpr (DEBUG) 7667136a5aeSJames Feist { 7677136a5aeSJames Feist debugPrint(); 7687136a5aeSJames Feist } 769c959c429SJames Feist if (zoneConfig.empty() || zoneDetailsConfig.empty()) 77050fdfe39SJames Feist { 77150fdfe39SJames Feist std::cerr << "No fan zones, application pausing until reboot\n"; 77250fdfe39SJames Feist while (1) 77350fdfe39SJames Feist { 77450fdfe39SJames Feist bus.process_discard(); 77565ea92e7SJames Feist bus.wait(); 77650fdfe39SJames Feist } 77750fdfe39SJames Feist } 7787136a5aeSJames Feist } 7797136a5aeSJames Feist } // namespace dbus_configuration 780