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 
3518b1311eSPatrick Venture extern std::map<std::string, struct SensorConfig> sensorConfig;
3618b1311eSPatrick Venture extern std::map<int64_t, PIDConf> zoneConfig;
3718b1311eSPatrick Venture extern std::map<int64_t, struct 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";
477136a5aeSJames Feist constexpr const char* sensorInterface = "xyz.openbmc_project.Sensor.Value";
487136a5aeSJames Feist constexpr const char* pwmInterface = "xyz.openbmc_project.Control.FanPwm";
497136a5aeSJames Feist 
507136a5aeSJames Feist namespace dbus_configuration
517136a5aeSJames Feist {
527136a5aeSJames Feist 
531738e2a2SJames Feist bool findSensors(const std::unordered_map<std::string, std::string>& sensors,
547136a5aeSJames Feist                  const std::string& search,
551738e2a2SJames Feist                  std::vector<std::pair<std::string, std::string>>& matches)
567136a5aeSJames Feist {
571738e2a2SJames Feist     std::smatch match;
581738e2a2SJames Feist     std::regex reg(search);
591738e2a2SJames Feist     for (const auto& sensor : sensors)
607136a5aeSJames Feist     {
611738e2a2SJames Feist         if (std::regex_search(sensor.first, match, reg))
621738e2a2SJames Feist         {
631738e2a2SJames Feist             matches.push_back(sensor);
641738e2a2SJames Feist         }
657136a5aeSJames Feist     }
66107a25daSPatrick Venture 
671738e2a2SJames Feist     return matches.size() > 0;
687136a5aeSJames Feist }
697136a5aeSJames Feist 
707136a5aeSJames Feist // this function prints the configuration into a form similar to the cpp
717136a5aeSJames Feist // generated code to help in verification, should be turned off during normal
727136a5aeSJames Feist // use
737136a5aeSJames Feist void debugPrint(void)
747136a5aeSJames Feist {
757136a5aeSJames Feist     // print sensor config
767136a5aeSJames Feist     std::cout << "sensor config:\n";
777136a5aeSJames Feist     std::cout << "{\n";
78c54fbd88SPatrick Venture     for (const auto& pair : sensorConfig)
797136a5aeSJames Feist     {
807136a5aeSJames Feist 
817136a5aeSJames Feist         std::cout << "\t{" << pair.first << ",\n\t\t{";
827136a5aeSJames Feist         std::cout << pair.second.type << ", ";
8369c51061SPatrick Venture         std::cout << pair.second.readPath << ", ";
8469c51061SPatrick Venture         std::cout << pair.second.writePath << ", ";
857136a5aeSJames Feist         std::cout << pair.second.min << ", ";
867136a5aeSJames Feist         std::cout << pair.second.max << ", ";
877136a5aeSJames Feist         std::cout << pair.second.timeout << "},\n\t},\n";
887136a5aeSJames Feist     }
897136a5aeSJames Feist     std::cout << "}\n\n";
907136a5aeSJames Feist     std::cout << "ZoneDetailsConfig\n";
917136a5aeSJames Feist     std::cout << "{\n";
92c54fbd88SPatrick Venture     for (const auto& zone : zoneDetailsConfig)
937136a5aeSJames Feist     {
947136a5aeSJames Feist         std::cout << "\t{" << zone.first << ",\n";
958e2fdb34SPatrick Venture         std::cout << "\t\t{" << zone.second.minThermalRpm << ", ";
968e2fdb34SPatrick Venture         std::cout << zone.second.failsafePercent << "}\n\t},\n";
977136a5aeSJames Feist     }
987136a5aeSJames Feist     std::cout << "}\n\n";
997136a5aeSJames Feist     std::cout << "ZoneConfig\n";
1007136a5aeSJames Feist     std::cout << "{\n";
101c54fbd88SPatrick Venture     for (const auto& zone : zoneConfig)
1027136a5aeSJames Feist     {
1037136a5aeSJames Feist         std::cout << "\t{" << zone.first << "\n";
1044a2dc4d8SPatrick Venture         for (const auto& pidconf : zone.second)
1057136a5aeSJames Feist         {
1067136a5aeSJames Feist             std::cout << "\t\t{" << pidconf.first << ",\n";
1077136a5aeSJames Feist             std::cout << "\t\t\t{" << pidconf.second.type << ",\n";
1087136a5aeSJames Feist             std::cout << "\t\t\t{";
1094a2dc4d8SPatrick Venture             for (const auto& input : pidconf.second.inputs)
1107136a5aeSJames Feist             {
1117136a5aeSJames Feist                 std::cout << "\n\t\t\t" << input << ",\n";
1127136a5aeSJames Feist             }
1137136a5aeSJames Feist             std::cout << "\t\t\t}\n";
1147136a5aeSJames Feist             std::cout << "\t\t\t" << pidconf.second.setpoint << ",\n";
11522c257abSJames Feist             std::cout << "\t\t\t{" << pidconf.second.pidInfo.ts << ",\n";
1167442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.proportionalCoeff
1177442c37aSPatrick Venture                       << ",\n";
1187442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.integralCoeff
1197442c37aSPatrick Venture                       << ",\n";
1207442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdOffset
1217442c37aSPatrick Venture                       << ",\n";
1227442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.feedFwdGain
1237442c37aSPatrick Venture                       << ",\n";
1247442c37aSPatrick Venture             std::cout << "\t\t\t{" << pidconf.second.pidInfo.integralLimit.min
1257442c37aSPatrick Venture                       << "," << pidconf.second.pidInfo.integralLimit.max
1267442c37aSPatrick Venture                       << "},\n";
1277442c37aSPatrick Venture             std::cout << "\t\t\t{" << pidconf.second.pidInfo.outLim.min << ","
1287442c37aSPatrick Venture                       << pidconf.second.pidInfo.outLim.max << "},\n";
1297442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.slewNeg << ",\n";
1307442c37aSPatrick Venture             std::cout << "\t\t\t" << pidconf.second.pidInfo.slewPos << ",\n";
1317136a5aeSJames Feist             std::cout << "\t\t\t}\n\t\t}\n";
1327136a5aeSJames Feist         }
1337136a5aeSJames Feist         std::cout << "\t},\n";
1347136a5aeSJames Feist     }
1357136a5aeSJames Feist     std::cout << "}\n\n";
1367136a5aeSJames Feist }
1377136a5aeSJames Feist 
13850fdfe39SJames Feist int eventHandler(sd_bus_message*, void*, sd_bus_error*)
13950fdfe39SJames Feist {
14050fdfe39SJames Feist     // do a brief sleep as we tend to get a bunch of these events at
14150fdfe39SJames Feist     // once
14250fdfe39SJames Feist     std::this_thread::sleep_for(std::chrono::seconds(5));
14350fdfe39SJames Feist     std::cout << "New configuration detected, restarting\n.";
14450fdfe39SJames Feist     std::exit(EXIT_SUCCESS); // service file should make us restart
14550fdfe39SJames Feist     return 1;
14650fdfe39SJames Feist }
14750fdfe39SJames Feist 
148ffd418bbSJames Feist size_t getZoneIndex(const std::string& name, std::vector<std::string>& zones)
149ffd418bbSJames Feist {
150ffd418bbSJames Feist     auto it = std::find(zones.begin(), zones.end(), name);
151ffd418bbSJames Feist     if (it == zones.end())
152ffd418bbSJames Feist     {
153ffd418bbSJames Feist         zones.emplace_back(name);
154ffd418bbSJames Feist         it = zones.end() - 1;
155ffd418bbSJames Feist     }
156ffd418bbSJames Feist 
157ffd418bbSJames Feist     return it - zones.begin();
158ffd418bbSJames Feist }
159ffd418bbSJames Feist 
1607136a5aeSJames Feist void init(sdbusplus::bus::bus& bus)
1617136a5aeSJames Feist {
16222c257abSJames Feist     using DbusVariantType =
1631f802f5eSJames Feist         std::variant<uint64_t, int64_t, double, std::string,
1641f802f5eSJames Feist                      std::vector<std::string>, std::vector<double>>;
16522c257abSJames Feist 
1667136a5aeSJames Feist     using ManagedObjectType = std::unordered_map<
1677136a5aeSJames Feist         sdbusplus::message::object_path,
1687136a5aeSJames Feist         std::unordered_map<std::string,
16922c257abSJames Feist                            std::unordered_map<std::string, DbusVariantType>>>;
17064f072a7SJames Feist 
17150fdfe39SJames Feist     // restart on configuration properties changed
17250fdfe39SJames Feist     static sdbusplus::bus::match::match configMatch(
17364f072a7SJames Feist         bus,
17464f072a7SJames Feist         "type='signal',member='PropertiesChanged',arg0namespace='" +
17564f072a7SJames Feist             std::string(pidConfigurationInterface) + "'",
17664f072a7SJames Feist         eventHandler);
17764f072a7SJames Feist 
17850fdfe39SJames Feist     // restart on sensors changed
17950fdfe39SJames Feist     static sdbusplus::bus::match::match sensorAdded(
18050fdfe39SJames Feist         bus,
18150fdfe39SJames Feist         "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
18250fdfe39SJames Feist         "sensors/'",
18350fdfe39SJames Feist         eventHandler);
18450fdfe39SJames Feist 
1857136a5aeSJames Feist     auto mapper =
1867136a5aeSJames Feist         bus.new_method_call("xyz.openbmc_project.ObjectMapper",
1877136a5aeSJames Feist                             "/xyz/openbmc_project/object_mapper",
1887136a5aeSJames Feist                             "xyz.openbmc_project.ObjectMapper", "GetSubTree");
18926e8c6a9SJames Feist     mapper.append("/", 0,
19022c257abSJames Feist                   std::array<const char*, 6>{objectManagerInterface,
1917136a5aeSJames Feist                                              pidConfigurationInterface,
1927136a5aeSJames Feist                                              pidZoneConfigurationInterface,
19322c257abSJames Feist                                              stepwiseConfigurationInterface,
1947136a5aeSJames Feist                                              sensorInterface, pwmInterface});
19522c257abSJames Feist     std::unordered_map<
19622c257abSJames Feist         std::string, std::unordered_map<std::string, std::vector<std::string>>>
19722c257abSJames Feist         respData;
19822c257abSJames Feist     try
19922c257abSJames Feist     {
2007136a5aeSJames Feist         auto resp = bus.call(mapper);
2017136a5aeSJames Feist         resp.read(respData);
20222c257abSJames Feist     }
20322c257abSJames Feist     catch (sdbusplus::exception_t&)
20422c257abSJames Feist     {
20522c257abSJames Feist         // can't do anything without mapper call data
20622c257abSJames Feist         throw std::runtime_error("ObjectMapper Call Failure");
20722c257abSJames Feist     }
20822c257abSJames Feist 
2097136a5aeSJames Feist     if (respData.empty())
2107136a5aeSJames Feist     {
21122c257abSJames Feist         // can't do anything without mapper call data
2127136a5aeSJames Feist         throw std::runtime_error("No configuration data available from Mapper");
2137136a5aeSJames Feist     }
2147136a5aeSJames Feist     // create a map of pair of <has pid configuration, ObjectManager path>
2157136a5aeSJames Feist     std::unordered_map<std::string, std::pair<bool, std::string>> owners;
2167136a5aeSJames Feist     // and a map of <path, interface> for sensors
2177136a5aeSJames Feist     std::unordered_map<std::string, std::string> sensors;
2187136a5aeSJames Feist     for (const auto& objectPair : respData)
2197136a5aeSJames Feist     {
2207136a5aeSJames Feist         for (const auto& ownerPair : objectPair.second)
2217136a5aeSJames Feist         {
2227136a5aeSJames Feist             auto& owner = owners[ownerPair.first];
2237136a5aeSJames Feist             for (const std::string& interface : ownerPair.second)
2247136a5aeSJames Feist             {
2257136a5aeSJames Feist 
2267136a5aeSJames Feist                 if (interface == objectManagerInterface)
2277136a5aeSJames Feist                 {
2287136a5aeSJames Feist                     owner.second = objectPair.first;
2297136a5aeSJames Feist                 }
2307136a5aeSJames Feist                 if (interface == pidConfigurationInterface ||
23122c257abSJames Feist                     interface == pidZoneConfigurationInterface ||
23222c257abSJames Feist                     interface == stepwiseConfigurationInterface)
2337136a5aeSJames Feist                 {
2347136a5aeSJames Feist                     owner.first = true;
2357136a5aeSJames Feist                 }
2367136a5aeSJames Feist                 if (interface == sensorInterface || interface == pwmInterface)
2377136a5aeSJames Feist                 {
2387136a5aeSJames Feist                     // we're not interested in pwm sensors, just pwm control
2397136a5aeSJames Feist                     if (interface == sensorInterface &&
2407136a5aeSJames Feist                         objectPair.first.find("pwm") != std::string::npos)
2417136a5aeSJames Feist                     {
2427136a5aeSJames Feist                         continue;
2437136a5aeSJames Feist                     }
2447136a5aeSJames Feist                     sensors[objectPair.first] = interface;
2457136a5aeSJames Feist                 }
2467136a5aeSJames Feist             }
2477136a5aeSJames Feist         }
2487136a5aeSJames Feist     }
2497136a5aeSJames Feist     ManagedObjectType configurations;
2507136a5aeSJames Feist     for (const auto& owner : owners)
2517136a5aeSJames Feist     {
2527136a5aeSJames Feist         // skip if no pid configuration (means probably a sensor)
2537136a5aeSJames Feist         if (!owner.second.first)
2547136a5aeSJames Feist         {
2557136a5aeSJames Feist             continue;
2567136a5aeSJames Feist         }
2577136a5aeSJames Feist         auto endpoint = bus.new_method_call(
2587136a5aeSJames Feist             owner.first.c_str(), owner.second.second.c_str(),
2597136a5aeSJames Feist             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
26022c257abSJames Feist         ManagedObjectType configuration;
26122c257abSJames Feist         try
26222c257abSJames Feist         {
2637136a5aeSJames Feist             auto responce = bus.call(endpoint);
2647136a5aeSJames Feist             responce.read(configuration);
26522c257abSJames Feist         }
26622c257abSJames Feist         catch (sdbusplus::exception_t&)
26722c257abSJames Feist         {
26822c257abSJames Feist             // this shouldn't happen, probably means daemon crashed
26922c257abSJames Feist             throw std::runtime_error("Error getting managed objects from " +
27022c257abSJames Feist                                      owner.first);
27122c257abSJames Feist         }
27222c257abSJames Feist 
2737136a5aeSJames Feist         for (auto& pathPair : configuration)
2747136a5aeSJames Feist         {
2757136a5aeSJames Feist             if (pathPair.second.find(pidConfigurationInterface) !=
2767136a5aeSJames Feist                     pathPair.second.end() ||
2777136a5aeSJames Feist                 pathPair.second.find(pidZoneConfigurationInterface) !=
27822c257abSJames Feist                     pathPair.second.end() ||
27922c257abSJames Feist                 pathPair.second.find(stepwiseConfigurationInterface) !=
2807136a5aeSJames Feist                     pathPair.second.end())
2817136a5aeSJames Feist             {
2827136a5aeSJames Feist                 configurations.emplace(pathPair);
2837136a5aeSJames Feist             }
2847136a5aeSJames Feist         }
2857136a5aeSJames Feist     }
2868c3c51eeSJames Feist 
2878c3c51eeSJames Feist     // on dbus having an index field is a bit strange, so randomly
2888c3c51eeSJames Feist     // assign index based on name property
289ffd418bbSJames Feist     std::vector<std::string> foundZones;
2907136a5aeSJames Feist     for (const auto& configuration : configurations)
2917136a5aeSJames Feist     {
2927136a5aeSJames Feist         auto findZone =
2937136a5aeSJames Feist             configuration.second.find(pidZoneConfigurationInterface);
2947136a5aeSJames Feist         if (findZone != configuration.second.end())
2957136a5aeSJames Feist         {
2967136a5aeSJames Feist             const auto& zone = findZone->second;
297ffd418bbSJames Feist 
2981f802f5eSJames Feist             const std::string& name = std::get<std::string>(zone.at("Name"));
299ffd418bbSJames Feist             size_t index = getZoneIndex(name, foundZones);
3008c3c51eeSJames Feist 
301c54fbd88SPatrick Venture             auto& details = zoneDetailsConfig[index];
3028e2fdb34SPatrick Venture             details.minThermalRpm =
3031f802f5eSJames Feist                 std::visit(VariantToDoubleVisitor(), zone.at("MinThermalRpm"));
3048e2fdb34SPatrick Venture             details.failsafePercent = std::visit(VariantToDoubleVisitor(),
3051f802f5eSJames Feist                                                  zone.at("FailSafePercent"));
3067136a5aeSJames Feist         }
3077136a5aeSJames Feist         auto findBase = configuration.second.find(pidConfigurationInterface);
30822c257abSJames Feist         if (findBase != configuration.second.end())
3097136a5aeSJames Feist         {
3108c3c51eeSJames Feist 
31122c257abSJames Feist             const auto& base =
31222c257abSJames Feist                 configuration.second.at(pidConfigurationInterface);
3138c3c51eeSJames Feist             const std::vector<std::string>& zones =
3141f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
3158c3c51eeSJames Feist             for (const std::string& zone : zones)
3168c3c51eeSJames Feist             {
317ffd418bbSJames Feist                 size_t index = getZoneIndex(zone, foundZones);
318c54fbd88SPatrick Venture                 PIDConf& conf = zoneConfig[index];
31950fdfe39SJames Feist 
32050fdfe39SJames Feist                 std::vector<std::string> sensorNames =
3211f802f5eSJames Feist                     std::get<std::vector<std::string>>(base.at("Inputs"));
32250fdfe39SJames Feist                 auto findOutputs =
32350fdfe39SJames Feist                     base.find("Outputs"); // currently only fans have outputs
32450fdfe39SJames Feist                 if (findOutputs != base.end())
32550fdfe39SJames Feist                 {
32650fdfe39SJames Feist                     std::vector<std::string> outputs =
3271f802f5eSJames Feist                         std::get<std::vector<std::string>>(findOutputs->second);
32850fdfe39SJames Feist                     sensorNames.insert(sensorNames.end(), outputs.begin(),
32950fdfe39SJames Feist                                        outputs.end());
33050fdfe39SJames Feist                 }
3311738e2a2SJames Feist 
33250fdfe39SJames Feist                 std::vector<std::string> inputs;
3331738e2a2SJames Feist                 std::vector<std::pair<std::string, std::string>>
3341738e2a2SJames Feist                     sensorInterfaces;
33550fdfe39SJames Feist                 for (const std::string& sensorName : sensorNames)
33650fdfe39SJames Feist                 {
33750fdfe39SJames Feist                     std::string name = sensorName;
33850fdfe39SJames Feist                     // replace spaces with underscores to be legal on dbus
33950fdfe39SJames Feist                     std::replace(name.begin(), name.end(), ' ', '_');
3401738e2a2SJames Feist                     findSensors(sensors, name, sensorInterfaces);
34150fdfe39SJames Feist                 }
3421738e2a2SJames Feist 
3431738e2a2SJames Feist                 // if the sensors aren't available in the current state, don't
3441738e2a2SJames Feist                 // add them to the configuration.
3451738e2a2SJames Feist                 if (sensorInterfaces.empty())
3461738e2a2SJames Feist                 {
3471738e2a2SJames Feist                     continue;
3481738e2a2SJames Feist                 }
3491738e2a2SJames Feist                 for (const auto& sensorPathIfacePair : sensorInterfaces)
3501738e2a2SJames Feist                 {
3511738e2a2SJames Feist 
35250fdfe39SJames Feist                     if (sensorPathIfacePair.second == sensorInterface)
35350fdfe39SJames Feist                     {
3541738e2a2SJames Feist                         size_t idx =
3551738e2a2SJames Feist                             sensorPathIfacePair.first.find_last_of("/") + 1;
3561738e2a2SJames Feist                         std::string shortName =
3571738e2a2SJames Feist                             sensorPathIfacePair.first.substr(idx);
3581738e2a2SJames Feist 
3591738e2a2SJames Feist                         inputs.push_back(shortName);
3601738e2a2SJames Feist                         auto& config = sensorConfig[shortName];
3611f802f5eSJames Feist                         config.type = std::get<std::string>(base.at("Class"));
36269c51061SPatrick Venture                         config.readPath = sensorPathIfacePair.first;
36350fdfe39SJames Feist                         // todo: maybe un-hardcode this if we run into slower
36450fdfe39SJames Feist                         // timeouts with sensors
36550fdfe39SJames Feist                         if (config.type == "temp")
36650fdfe39SJames Feist                         {
367*2642cb54SJames Feist                             config.timeout = 0;
36850fdfe39SJames Feist                         }
36950fdfe39SJames Feist                     }
37050fdfe39SJames Feist                     else if (sensorPathIfacePair.second == pwmInterface)
37150fdfe39SJames Feist                     {
37250fdfe39SJames Feist                         // copy so we can modify it
37350fdfe39SJames Feist                         for (std::string otherSensor : sensorNames)
37450fdfe39SJames Feist                         {
3751738e2a2SJames Feist                             std::replace(otherSensor.begin(), otherSensor.end(),
3761738e2a2SJames Feist                                          ' ', '_');
3771738e2a2SJames Feist                             if (sensorPathIfacePair.first.find(otherSensor) !=
3781738e2a2SJames Feist                                 std::string::npos)
37950fdfe39SJames Feist                             {
38050fdfe39SJames Feist                                 continue;
38150fdfe39SJames Feist                             }
3821738e2a2SJames Feist 
383c54fbd88SPatrick Venture                             auto& config = sensorConfig[otherSensor];
38469c51061SPatrick Venture                             config.writePath = sensorPathIfacePair.first;
38550fdfe39SJames Feist                             // todo: un-hardcode this if there are fans with
38650fdfe39SJames Feist                             // different ranges
38750fdfe39SJames Feist                             config.max = 255;
38850fdfe39SJames Feist                             config.min = 0;
38950fdfe39SJames Feist                         }
39050fdfe39SJames Feist                     }
39150fdfe39SJames Feist                 }
3921738e2a2SJames Feist 
393f3252315SPatrick Venture                 struct ControllerInfo& info =
3941f802f5eSJames Feist                     conf[std::get<std::string>(base.at("Name"))];
39550fdfe39SJames Feist                 info.inputs = std::move(inputs);
39650fdfe39SJames Feist 
3971f802f5eSJames Feist                 info.type = std::get<std::string>(base.at("Class"));
3988c3c51eeSJames Feist                 // todo: auto generation yaml -> c script seems to discard this
3998c3c51eeSJames Feist                 // value for fans, verify this is okay
4007136a5aeSJames Feist                 if (info.type == "fan")
4017136a5aeSJames Feist                 {
4027136a5aeSJames Feist                     info.setpoint = 0;
4037136a5aeSJames Feist                 }
4047136a5aeSJames Feist                 else
4057136a5aeSJames Feist                 {
4061f802f5eSJames Feist                     info.setpoint = std::visit(VariantToDoubleVisitor(),
407208abce8SJames Feist                                                base.at("SetPoint"));
4087136a5aeSJames Feist                 }
40922c257abSJames Feist                 info.pidInfo.ts = 1.0; // currently unused
4107442c37aSPatrick Venture                 info.pidInfo.proportionalCoeff = std::visit(
4117442c37aSPatrick Venture                     VariantToDoubleVisitor(), base.at("PCoefficient"));
4127442c37aSPatrick Venture                 info.pidInfo.integralCoeff = std::visit(
4137442c37aSPatrick Venture                     VariantToDoubleVisitor(), base.at("ICoefficient"));
4147442c37aSPatrick Venture                 info.pidInfo.feedFwdOffset = std::visit(
4157442c37aSPatrick Venture                     VariantToDoubleVisitor(), base.at("FFOffCoefficient"));
4167442c37aSPatrick Venture                 info.pidInfo.feedFwdGain = std::visit(
4177442c37aSPatrick Venture                     VariantToDoubleVisitor(), base.at("FFGainCoefficient"));
4187442c37aSPatrick Venture                 info.pidInfo.integralLimit.max =
4191f802f5eSJames Feist                     std::visit(VariantToDoubleVisitor(), base.at("ILimitMax"));
4207442c37aSPatrick Venture                 info.pidInfo.integralLimit.min =
4211f802f5eSJames Feist                     std::visit(VariantToDoubleVisitor(), base.at("ILimitMin"));
4227442c37aSPatrick Venture                 info.pidInfo.outLim.max = std::visit(VariantToDoubleVisitor(),
4231f802f5eSJames Feist                                                      base.at("OutLimitMax"));
4247442c37aSPatrick Venture                 info.pidInfo.outLim.min = std::visit(VariantToDoubleVisitor(),
4251f802f5eSJames Feist                                                      base.at("OutLimitMin"));
4267442c37aSPatrick Venture                 info.pidInfo.slewNeg =
4271f802f5eSJames Feist                     std::visit(VariantToDoubleVisitor(), base.at("SlewNeg"));
4287442c37aSPatrick Venture                 info.pidInfo.slewPos =
4291f802f5eSJames Feist                     std::visit(VariantToDoubleVisitor(), base.at("SlewPos"));
430572c43daSJames Feist                 double negativeHysteresis = 0;
431572c43daSJames Feist                 double positiveHysteresis = 0;
432572c43daSJames Feist 
433572c43daSJames Feist                 auto findNeg = base.find("NegativeHysteresis");
434572c43daSJames Feist                 auto findPos = base.find("PositiveHysteresis");
435572c43daSJames Feist                 if (findNeg != base.end())
436572c43daSJames Feist                 {
4371f802f5eSJames Feist                     negativeHysteresis =
4381f802f5eSJames Feist                         std::visit(VariantToDoubleVisitor(), findNeg->second);
439572c43daSJames Feist                 }
440572c43daSJames Feist 
441572c43daSJames Feist                 if (findPos != base.end())
442572c43daSJames Feist                 {
4431f802f5eSJames Feist                     positiveHysteresis =
4441f802f5eSJames Feist                         std::visit(VariantToDoubleVisitor(), findPos->second);
445572c43daSJames Feist                 }
446572c43daSJames Feist                 info.pidInfo.negativeHysteresis = negativeHysteresis;
447572c43daSJames Feist                 info.pidInfo.positiveHysteresis = positiveHysteresis;
4487136a5aeSJames Feist             }
4498c3c51eeSJames Feist         }
45022c257abSJames Feist         auto findStepwise =
45122c257abSJames Feist             configuration.second.find(stepwiseConfigurationInterface);
45222c257abSJames Feist         if (findStepwise != configuration.second.end())
45322c257abSJames Feist         {
45422c257abSJames Feist             const auto& base = findStepwise->second;
45522c257abSJames Feist             const std::vector<std::string>& zones =
4561f802f5eSJames Feist                 std::get<std::vector<std::string>>(base.at("Zones"));
45722c257abSJames Feist             for (const std::string& zone : zones)
45822c257abSJames Feist             {
459ffd418bbSJames Feist                 size_t index = getZoneIndex(zone, foundZones);
460c54fbd88SPatrick Venture                 PIDConf& conf = zoneConfig[index];
46150fdfe39SJames Feist 
46250fdfe39SJames Feist                 std::vector<std::string> inputs;
46350fdfe39SJames Feist                 std::vector<std::string> sensorNames =
4641f802f5eSJames Feist                     std::get<std::vector<std::string>>(base.at("Inputs"));
46550fdfe39SJames Feist 
4661738e2a2SJames Feist                 bool sensorFound = false;
46750fdfe39SJames Feist                 for (const std::string& sensorName : sensorNames)
46850fdfe39SJames Feist                 {
46950fdfe39SJames Feist                     std::string name = sensorName;
47050fdfe39SJames Feist                     // replace spaces with underscores to be legal on dbus
47150fdfe39SJames Feist                     std::replace(name.begin(), name.end(), ' ', '_');
4721738e2a2SJames Feist                     std::vector<std::pair<std::string, std::string>>
4731738e2a2SJames Feist                         sensorPathIfacePairs;
47450fdfe39SJames Feist 
4751738e2a2SJames Feist                     if (!findSensors(sensors, name, sensorPathIfacePairs))
47650fdfe39SJames Feist                     {
47750fdfe39SJames Feist                         break;
47850fdfe39SJames Feist                     }
47950fdfe39SJames Feist 
4801738e2a2SJames Feist                     for (const auto& sensorPathIfacePair : sensorPathIfacePairs)
4811738e2a2SJames Feist                     {
4821738e2a2SJames Feist                         size_t idx =
4831738e2a2SJames Feist                             sensorPathIfacePair.first.find_last_of("/") + 1;
4841738e2a2SJames Feist                         std::string shortName =
4851738e2a2SJames Feist                             sensorPathIfacePair.first.substr(idx);
4861738e2a2SJames Feist 
4871738e2a2SJames Feist                         inputs.push_back(shortName);
4881738e2a2SJames Feist                         auto& config = sensorConfig[shortName];
48969c51061SPatrick Venture                         config.readPath = sensorPathIfacePair.first;
49050fdfe39SJames Feist                         config.type = "temp";
49150fdfe39SJames Feist                         // todo: maybe un-hardcode this if we run into slower
49250fdfe39SJames Feist                         // timeouts with sensors
49350fdfe39SJames Feist 
494*2642cb54SJames Feist                         config.timeout = 0;
4951738e2a2SJames Feist                         sensorFound = true;
4961738e2a2SJames Feist                     }
49750fdfe39SJames Feist                 }
49850fdfe39SJames Feist                 if (!sensorFound)
49950fdfe39SJames Feist                 {
50050fdfe39SJames Feist                     continue;
50150fdfe39SJames Feist                 }
502f3252315SPatrick Venture                 struct ControllerInfo& info =
5031f802f5eSJames Feist                     conf[std::get<std::string>(base.at("Name"))];
50450fdfe39SJames Feist                 info.inputs = std::move(inputs);
50550fdfe39SJames Feist 
50622c257abSJames Feist                 info.type = "stepwise";
50722c257abSJames Feist                 info.stepwiseInfo.ts = 1.0; // currently unused
5083dfaafdaSJames Feist                 info.stepwiseInfo.positiveHysteresis = 0.0;
5093dfaafdaSJames Feist                 info.stepwiseInfo.negativeHysteresis = 0.0;
5103dfaafdaSJames Feist                 auto findPosHyst = base.find("PositiveHysteresis");
5113dfaafdaSJames Feist                 auto findNegHyst = base.find("NegativeHysteresis");
5123dfaafdaSJames Feist                 if (findPosHyst != base.end())
5133dfaafdaSJames Feist                 {
5141f802f5eSJames Feist                     info.stepwiseInfo.positiveHysteresis = std::visit(
515208abce8SJames Feist                         VariantToDoubleVisitor(), findPosHyst->second);
5163dfaafdaSJames Feist                 }
5173dfaafdaSJames Feist                 if (findNegHyst != base.end())
5183dfaafdaSJames Feist                 {
5191f802f5eSJames Feist                     info.stepwiseInfo.positiveHysteresis = std::visit(
520208abce8SJames Feist                         VariantToDoubleVisitor(), findNegHyst->second);
5213dfaafdaSJames Feist                 }
52222c257abSJames Feist                 std::vector<double> readings =
5231f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Reading"));
52422c257abSJames Feist                 if (readings.size() > ec::maxStepwisePoints)
52522c257abSJames Feist                 {
52622c257abSJames Feist                     throw std::invalid_argument("Too many stepwise points.");
52722c257abSJames Feist                 }
52822c257abSJames Feist                 if (readings.empty())
52922c257abSJames Feist                 {
53022c257abSJames Feist                     throw std::invalid_argument(
53122c257abSJames Feist                         "Must have one stepwise point.");
53222c257abSJames Feist                 }
53322c257abSJames Feist                 std::copy(readings.begin(), readings.end(),
53422c257abSJames Feist                           info.stepwiseInfo.reading);
53522c257abSJames Feist                 if (readings.size() < ec::maxStepwisePoints)
53622c257abSJames Feist                 {
53722c257abSJames Feist                     info.stepwiseInfo.reading[readings.size()] =
5385f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
53922c257abSJames Feist                 }
54022c257abSJames Feist                 std::vector<double> outputs =
5411f802f5eSJames Feist                     std::get<std::vector<double>>(base.at("Output"));
54222c257abSJames Feist                 if (readings.size() != outputs.size())
54322c257abSJames Feist                 {
54422c257abSJames Feist                     throw std::invalid_argument(
54522c257abSJames Feist                         "Outputs size must match readings");
54622c257abSJames Feist                 }
54722c257abSJames Feist                 std::copy(outputs.begin(), outputs.end(),
54822c257abSJames Feist                           info.stepwiseInfo.output);
54922c257abSJames Feist                 if (outputs.size() < ec::maxStepwisePoints)
55022c257abSJames Feist                 {
55122c257abSJames Feist                     info.stepwiseInfo.output[outputs.size()] =
5525f59c0fdSPatrick Venture                         std::numeric_limits<double>::quiet_NaN();
55322c257abSJames Feist                 }
55422c257abSJames Feist             }
55522c257abSJames Feist         }
55622c257abSJames Feist     }
5577136a5aeSJames Feist     if (DEBUG)
5587136a5aeSJames Feist     {
5597136a5aeSJames Feist         debugPrint();
5607136a5aeSJames Feist     }
561c959c429SJames Feist     if (zoneConfig.empty() || zoneDetailsConfig.empty())
56250fdfe39SJames Feist     {
56350fdfe39SJames Feist         std::cerr << "No fan zones, application pausing until reboot\n";
56450fdfe39SJames Feist         while (1)
56550fdfe39SJames Feist         {
56650fdfe39SJames Feist             bus.process_discard();
56765ea92e7SJames Feist             bus.wait();
56850fdfe39SJames Feist         }
56950fdfe39SJames Feist     }
5707136a5aeSJames Feist }
5717136a5aeSJames Feist } // namespace dbus_configuration
572