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>
247136a5aeSJames Feist #include <sdbusplus/bus.hpp>
2564f072a7SJames Feist #include <sdbusplus/bus/match.hpp>
2622c257abSJames Feist #include <sdbusplus/exception.hpp>
277136a5aeSJames Feist #include <set>
2864f072a7SJames Feist #include <thread>
297136a5aeSJames Feist #include <unordered_map>
307136a5aeSJames Feist 
317136a5aeSJames Feist static constexpr bool DEBUG = false; // enable to print found configuration
327136a5aeSJames Feist 
33*f3252315SPatrick Venture std::map<std::string, struct SensorConfig> SensorConfig = {};
347136a5aeSJames Feist std::map<int64_t, PIDConf> ZoneConfig = {};
35*f3252315SPatrick Venture std::map<int64_t, struct ZoneConfig> ZoneDetailsConfig = {};
367136a5aeSJames Feist 
377136a5aeSJames Feist constexpr const char* pidConfigurationInterface =
387136a5aeSJames Feist     "xyz.openbmc_project.Configuration.Pid";
397136a5aeSJames Feist constexpr const char* objectManagerInterface =
407136a5aeSJames Feist     "org.freedesktop.DBus.ObjectManager";
417136a5aeSJames Feist constexpr const char* pidZoneConfigurationInterface =
427136a5aeSJames Feist     "xyz.openbmc_project.Configuration.Pid.Zone";
4322c257abSJames Feist constexpr const char* stepwiseConfigurationInterface =
4422c257abSJames Feist     "xyz.openbmc_project.Configuration.Stepwise";
457136a5aeSJames Feist constexpr const char* sensorInterface = "xyz.openbmc_project.Sensor.Value";
467136a5aeSJames Feist constexpr const char* pwmInterface = "xyz.openbmc_project.Control.FanPwm";
477136a5aeSJames Feist 
487136a5aeSJames Feist namespace dbus_configuration
497136a5aeSJames Feist {
507136a5aeSJames Feist 
5150fdfe39SJames Feist namespace variant_ns = sdbusplus::message::variant_ns;
5250fdfe39SJames Feist 
537136a5aeSJames Feist bool findSensor(const std::unordered_map<std::string, std::string>& sensors,
547136a5aeSJames Feist                 const std::string& search,
557136a5aeSJames Feist                 std::pair<std::string, std::string>& sensor)
567136a5aeSJames Feist {
57107a25daSPatrick Venture     auto found =
58107a25daSPatrick Venture         std::find_if(sensors.begin(), sensors.end(), [&search](const auto& s) {
59107a25daSPatrick Venture             return (s.first.find(search) != std::string::npos);
60107a25daSPatrick Venture         });
61107a25daSPatrick Venture     if (found != sensors.end())
627136a5aeSJames Feist     {
63107a25daSPatrick Venture         sensor = *found;
647136a5aeSJames Feist         return true;
657136a5aeSJames Feist     }
66107a25daSPatrick Venture 
677136a5aeSJames Feist     return false;
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";
784a2dc4d8SPatrick 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 << ", ";
837136a5aeSJames Feist         std::cout << pair.second.readpath << ", ";
847136a5aeSJames Feist         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";
924a2dc4d8SPatrick Venture     for (const auto& zone : ZoneDetailsConfig)
937136a5aeSJames Feist     {
947136a5aeSJames Feist         std::cout << "\t{" << zone.first << ",\n";
957136a5aeSJames Feist         std::cout << "\t\t{" << zone.second.minthermalrpm << ", ";
967136a5aeSJames Feist         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";
1014a2dc4d8SPatrick 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";
11622c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.p_c << ",\n";
11722c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.i_c << ",\n";
11822c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.ff_off << ",\n";
11922c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.ff_gain << ",\n";
12022c257abSJames Feist             std::cout << "\t\t\t{" << pidconf.second.pidInfo.i_lim.min << ","
12122c257abSJames Feist                       << pidconf.second.pidInfo.i_lim.max << "},\n";
12222c257abSJames Feist             std::cout << "\t\t\t{" << pidconf.second.pidInfo.out_lim.min << ","
12322c257abSJames Feist                       << pidconf.second.pidInfo.out_lim.max << "},\n";
12422c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.slew_neg << ",\n";
12522c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.slew_pos << ",\n";
1267136a5aeSJames Feist             std::cout << "\t\t\t}\n\t\t}\n";
1277136a5aeSJames Feist         }
1287136a5aeSJames Feist         std::cout << "\t},\n";
1297136a5aeSJames Feist     }
1307136a5aeSJames Feist     std::cout << "}\n\n";
1317136a5aeSJames Feist }
1327136a5aeSJames Feist 
13350fdfe39SJames Feist int eventHandler(sd_bus_message*, void*, sd_bus_error*)
13450fdfe39SJames Feist {
13550fdfe39SJames Feist     // do a brief sleep as we tend to get a bunch of these events at
13650fdfe39SJames Feist     // once
13750fdfe39SJames Feist     std::this_thread::sleep_for(std::chrono::seconds(5));
13850fdfe39SJames Feist     std::cout << "New configuration detected, restarting\n.";
13950fdfe39SJames Feist     std::exit(EXIT_SUCCESS); // service file should make us restart
14050fdfe39SJames Feist     return 1;
14150fdfe39SJames Feist }
14250fdfe39SJames Feist 
1437136a5aeSJames Feist void init(sdbusplus::bus::bus& bus)
1447136a5aeSJames Feist {
14522c257abSJames Feist     using DbusVariantType =
14622c257abSJames Feist         sdbusplus::message::variant<uint64_t, int64_t, double, std::string,
14722c257abSJames Feist                                     std::vector<std::string>,
14822c257abSJames Feist                                     std::vector<double>>;
14922c257abSJames Feist 
1507136a5aeSJames Feist     using ManagedObjectType = std::unordered_map<
1517136a5aeSJames Feist         sdbusplus::message::object_path,
1527136a5aeSJames Feist         std::unordered_map<std::string,
15322c257abSJames Feist                            std::unordered_map<std::string, DbusVariantType>>>;
15464f072a7SJames Feist 
15550fdfe39SJames Feist     // restart on configuration properties changed
15650fdfe39SJames Feist     static sdbusplus::bus::match::match configMatch(
15764f072a7SJames Feist         bus,
15864f072a7SJames Feist         "type='signal',member='PropertiesChanged',arg0namespace='" +
15964f072a7SJames Feist             std::string(pidConfigurationInterface) + "'",
16064f072a7SJames Feist         eventHandler);
16164f072a7SJames Feist 
16250fdfe39SJames Feist     // restart on sensors changed
16350fdfe39SJames Feist     static sdbusplus::bus::match::match sensorAdded(
16450fdfe39SJames Feist         bus,
16550fdfe39SJames Feist         "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
16650fdfe39SJames Feist         "sensors/'",
16750fdfe39SJames Feist         eventHandler);
16850fdfe39SJames Feist 
1697136a5aeSJames Feist     auto mapper =
1707136a5aeSJames Feist         bus.new_method_call("xyz.openbmc_project.ObjectMapper",
1717136a5aeSJames Feist                             "/xyz/openbmc_project/object_mapper",
1727136a5aeSJames Feist                             "xyz.openbmc_project.ObjectMapper", "GetSubTree");
17326e8c6a9SJames Feist     mapper.append("/", 0,
17422c257abSJames Feist                   std::array<const char*, 6>{objectManagerInterface,
1757136a5aeSJames Feist                                              pidConfigurationInterface,
1767136a5aeSJames Feist                                              pidZoneConfigurationInterface,
17722c257abSJames Feist                                              stepwiseConfigurationInterface,
1787136a5aeSJames Feist                                              sensorInterface, pwmInterface});
17922c257abSJames Feist     std::unordered_map<
18022c257abSJames Feist         std::string, std::unordered_map<std::string, std::vector<std::string>>>
18122c257abSJames Feist         respData;
18222c257abSJames Feist     try
18322c257abSJames Feist     {
1847136a5aeSJames Feist         auto resp = bus.call(mapper);
1857136a5aeSJames Feist         if (resp.is_method_error())
1867136a5aeSJames Feist         {
1877136a5aeSJames Feist             throw std::runtime_error("ObjectMapper Call Failure");
1887136a5aeSJames Feist         }
1897136a5aeSJames Feist         resp.read(respData);
19022c257abSJames Feist     }
19122c257abSJames Feist     catch (sdbusplus::exception_t&)
19222c257abSJames Feist     {
19322c257abSJames Feist         // can't do anything without mapper call data
19422c257abSJames Feist         throw std::runtime_error("ObjectMapper Call Failure");
19522c257abSJames Feist     }
19622c257abSJames Feist 
1977136a5aeSJames Feist     if (respData.empty())
1987136a5aeSJames Feist     {
19922c257abSJames Feist         // can't do anything without mapper call data
2007136a5aeSJames Feist         throw std::runtime_error("No configuration data available from Mapper");
2017136a5aeSJames Feist     }
2027136a5aeSJames Feist     // create a map of pair of <has pid configuration, ObjectManager path>
2037136a5aeSJames Feist     std::unordered_map<std::string, std::pair<bool, std::string>> owners;
2047136a5aeSJames Feist     // and a map of <path, interface> for sensors
2057136a5aeSJames Feist     std::unordered_map<std::string, std::string> sensors;
2067136a5aeSJames Feist     for (const auto& objectPair : respData)
2077136a5aeSJames Feist     {
2087136a5aeSJames Feist         for (const auto& ownerPair : objectPair.second)
2097136a5aeSJames Feist         {
2107136a5aeSJames Feist             auto& owner = owners[ownerPair.first];
2117136a5aeSJames Feist             for (const std::string& interface : ownerPair.second)
2127136a5aeSJames Feist             {
2137136a5aeSJames Feist 
2147136a5aeSJames Feist                 if (interface == objectManagerInterface)
2157136a5aeSJames Feist                 {
2167136a5aeSJames Feist                     owner.second = objectPair.first;
2177136a5aeSJames Feist                 }
2187136a5aeSJames Feist                 if (interface == pidConfigurationInterface ||
21922c257abSJames Feist                     interface == pidZoneConfigurationInterface ||
22022c257abSJames Feist                     interface == stepwiseConfigurationInterface)
2217136a5aeSJames Feist                 {
2227136a5aeSJames Feist                     owner.first = true;
2237136a5aeSJames Feist                 }
2247136a5aeSJames Feist                 if (interface == sensorInterface || interface == pwmInterface)
2257136a5aeSJames Feist                 {
2267136a5aeSJames Feist                     // we're not interested in pwm sensors, just pwm control
2277136a5aeSJames Feist                     if (interface == sensorInterface &&
2287136a5aeSJames Feist                         objectPair.first.find("pwm") != std::string::npos)
2297136a5aeSJames Feist                     {
2307136a5aeSJames Feist                         continue;
2317136a5aeSJames Feist                     }
2327136a5aeSJames Feist                     sensors[objectPair.first] = interface;
2337136a5aeSJames Feist                 }
2347136a5aeSJames Feist             }
2357136a5aeSJames Feist         }
2367136a5aeSJames Feist     }
2377136a5aeSJames Feist     ManagedObjectType configurations;
2387136a5aeSJames Feist     for (const auto& owner : owners)
2397136a5aeSJames Feist     {
2407136a5aeSJames Feist         // skip if no pid configuration (means probably a sensor)
2417136a5aeSJames Feist         if (!owner.second.first)
2427136a5aeSJames Feist         {
2437136a5aeSJames Feist             continue;
2447136a5aeSJames Feist         }
2457136a5aeSJames Feist         auto endpoint = bus.new_method_call(
2467136a5aeSJames Feist             owner.first.c_str(), owner.second.second.c_str(),
2477136a5aeSJames Feist             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
24822c257abSJames Feist         ManagedObjectType configuration;
24922c257abSJames Feist         try
25022c257abSJames Feist         {
2517136a5aeSJames Feist             auto responce = bus.call(endpoint);
2527136a5aeSJames Feist             if (responce.is_method_error())
2537136a5aeSJames Feist             {
2547136a5aeSJames Feist                 throw std::runtime_error("Error getting managed objects from " +
2557136a5aeSJames Feist                                          owner.first);
2567136a5aeSJames Feist             }
2577136a5aeSJames Feist             responce.read(configuration);
25822c257abSJames Feist         }
25922c257abSJames Feist         catch (sdbusplus::exception_t&)
26022c257abSJames Feist         {
26122c257abSJames Feist             // this shouldn't happen, probably means daemon crashed
26222c257abSJames Feist             throw std::runtime_error("Error getting managed objects from " +
26322c257abSJames Feist                                      owner.first);
26422c257abSJames Feist         }
26522c257abSJames Feist 
2667136a5aeSJames Feist         for (auto& pathPair : configuration)
2677136a5aeSJames Feist         {
2687136a5aeSJames Feist             if (pathPair.second.find(pidConfigurationInterface) !=
2697136a5aeSJames Feist                     pathPair.second.end() ||
2707136a5aeSJames Feist                 pathPair.second.find(pidZoneConfigurationInterface) !=
27122c257abSJames Feist                     pathPair.second.end() ||
27222c257abSJames Feist                 pathPair.second.find(stepwiseConfigurationInterface) !=
2737136a5aeSJames Feist                     pathPair.second.end())
2747136a5aeSJames Feist             {
2757136a5aeSJames Feist                 configurations.emplace(pathPair);
2767136a5aeSJames Feist             }
2777136a5aeSJames Feist         }
2787136a5aeSJames Feist     }
2798c3c51eeSJames Feist 
2808c3c51eeSJames Feist     // on dbus having an index field is a bit strange, so randomly
2818c3c51eeSJames Feist     // assign index based on name property
2828c3c51eeSJames Feist     std::vector<std::string> zoneIndex;
2837136a5aeSJames Feist     for (const auto& configuration : configurations)
2847136a5aeSJames Feist     {
2857136a5aeSJames Feist         auto findZone =
2867136a5aeSJames Feist             configuration.second.find(pidZoneConfigurationInterface);
2877136a5aeSJames Feist         if (findZone != configuration.second.end())
2887136a5aeSJames Feist         {
2897136a5aeSJames Feist             const auto& zone = findZone->second;
2908c3c51eeSJames Feist             size_t index = 1;
2918c3c51eeSJames Feist             const std::string& name =
29250fdfe39SJames Feist                 variant_ns::get<std::string>(zone.at("Name"));
2938c3c51eeSJames Feist             auto it = std::find(zoneIndex.begin(), zoneIndex.end(), name);
2948c3c51eeSJames Feist             if (it == zoneIndex.end())
2958c3c51eeSJames Feist             {
2968c3c51eeSJames Feist                 zoneIndex.emplace_back(name);
2978c3c51eeSJames Feist                 index = zoneIndex.size();
2988c3c51eeSJames Feist             }
2998c3c51eeSJames Feist             else
3008c3c51eeSJames Feist             {
3018c3c51eeSJames Feist                 index = zoneIndex.end() - it;
3028c3c51eeSJames Feist             }
3038c3c51eeSJames Feist 
3048c3c51eeSJames Feist             auto& details = ZoneDetailsConfig[index];
305d7a55bfdSJames Feist             details.minthermalrpm = variant_ns::apply_visitor(
3067136a5aeSJames Feist                 VariantToFloatVisitor(), zone.at("MinThermalRpm"));
307d7a55bfdSJames Feist             details.failsafepercent = variant_ns::apply_visitor(
3087136a5aeSJames Feist                 VariantToFloatVisitor(), zone.at("FailSafePercent"));
3097136a5aeSJames Feist         }
3107136a5aeSJames Feist         auto findBase = configuration.second.find(pidConfigurationInterface);
31122c257abSJames Feist         if (findBase != configuration.second.end())
3127136a5aeSJames Feist         {
3138c3c51eeSJames Feist 
31422c257abSJames Feist             const auto& base =
31522c257abSJames Feist                 configuration.second.at(pidConfigurationInterface);
3168c3c51eeSJames Feist             const std::vector<std::string>& zones =
31750fdfe39SJames Feist                 variant_ns::get<std::vector<std::string>>(base.at("Zones"));
3188c3c51eeSJames Feist             for (const std::string& zone : zones)
3198c3c51eeSJames Feist             {
3208c3c51eeSJames Feist                 auto it = std::find(zoneIndex.begin(), zoneIndex.end(), zone);
3218c3c51eeSJames Feist                 size_t index = 1;
3228c3c51eeSJames Feist                 if (it == zoneIndex.end())
3238c3c51eeSJames Feist                 {
3248c3c51eeSJames Feist                     zoneIndex.emplace_back(zone);
3258c3c51eeSJames Feist                     index = zoneIndex.size();
3268c3c51eeSJames Feist                 }
3278c3c51eeSJames Feist                 else
3288c3c51eeSJames Feist                 {
3298c3c51eeSJames Feist                     index = zoneIndex.end() - it;
3308c3c51eeSJames Feist                 }
3318c3c51eeSJames Feist                 PIDConf& conf = ZoneConfig[index];
33250fdfe39SJames Feist 
33350fdfe39SJames Feist                 std::vector<std::string> sensorNames =
33450fdfe39SJames Feist                     variant_ns::get<std::vector<std::string>>(
33550fdfe39SJames Feist                         base.at("Inputs"));
33650fdfe39SJames Feist                 auto findOutputs =
33750fdfe39SJames Feist                     base.find("Outputs"); // currently only fans have outputs
33850fdfe39SJames Feist                 if (findOutputs != base.end())
33950fdfe39SJames Feist                 {
34050fdfe39SJames Feist                     std::vector<std::string> outputs =
34150fdfe39SJames Feist                         variant_ns::get<std::vector<std::string>>(
34250fdfe39SJames Feist                             findOutputs->second);
34350fdfe39SJames Feist                     sensorNames.insert(sensorNames.end(), outputs.begin(),
34450fdfe39SJames Feist                                        outputs.end());
34550fdfe39SJames Feist                 }
34650fdfe39SJames Feist                 bool sensorsAvailable = sensorNames.size();
34750fdfe39SJames Feist                 std::vector<std::string> inputs;
34850fdfe39SJames Feist                 for (const std::string& sensorName : sensorNames)
34950fdfe39SJames Feist                 {
35050fdfe39SJames Feist                     std::string name = sensorName;
35150fdfe39SJames Feist                     // replace spaces with underscores to be legal on dbus
35250fdfe39SJames Feist                     std::replace(name.begin(), name.end(), ' ', '_');
35350fdfe39SJames Feist                     std::pair<std::string, std::string> sensorPathIfacePair;
35450fdfe39SJames Feist 
35550fdfe39SJames Feist                     if (!findSensor(sensors, name, sensorPathIfacePair))
35650fdfe39SJames Feist                     {
35750fdfe39SJames Feist                         sensorsAvailable = false;
35850fdfe39SJames Feist                         break;
35950fdfe39SJames Feist                     }
36050fdfe39SJames Feist                     if (sensorPathIfacePair.second == sensorInterface)
36150fdfe39SJames Feist                     {
36250fdfe39SJames Feist                         inputs.push_back(name);
36350fdfe39SJames Feist                         auto& config = SensorConfig[name];
36450fdfe39SJames Feist                         config.type =
36550fdfe39SJames Feist                             variant_ns::get<std::string>(base.at("Class"));
36650fdfe39SJames Feist                         config.readpath = sensorPathIfacePair.first;
36750fdfe39SJames Feist                         // todo: maybe un-hardcode this if we run into slower
36850fdfe39SJames Feist                         // timeouts with sensors
36950fdfe39SJames Feist                         if (config.type == "temp")
37050fdfe39SJames Feist                         {
37150fdfe39SJames Feist                             config.timeout = 500;
37250fdfe39SJames Feist                         }
37350fdfe39SJames Feist                     }
37450fdfe39SJames Feist                     else if (sensorPathIfacePair.second == pwmInterface)
37550fdfe39SJames Feist                     {
37650fdfe39SJames Feist                         // copy so we can modify it
37750fdfe39SJames Feist                         for (std::string otherSensor : sensorNames)
37850fdfe39SJames Feist                         {
37950fdfe39SJames Feist                             if (otherSensor == sensorName)
38050fdfe39SJames Feist                             {
38150fdfe39SJames Feist                                 continue;
38250fdfe39SJames Feist                             }
38350fdfe39SJames Feist                             std::replace(otherSensor.begin(), otherSensor.end(),
38450fdfe39SJames Feist                                          ' ', '_');
38550fdfe39SJames Feist                             auto& config = SensorConfig[otherSensor];
38650fdfe39SJames Feist                             config.writepath = sensorPathIfacePair.first;
38750fdfe39SJames Feist                             // todo: un-hardcode this if there are fans with
38850fdfe39SJames Feist                             // different ranges
38950fdfe39SJames Feist                             config.max = 255;
39050fdfe39SJames Feist                             config.min = 0;
39150fdfe39SJames Feist                         }
39250fdfe39SJames Feist                     }
39350fdfe39SJames Feist                 }
39450fdfe39SJames Feist                 // if the sensors aren't available in the current state, don't
39550fdfe39SJames Feist                 // add them to the configuration.
39650fdfe39SJames Feist                 if (!sensorsAvailable)
39750fdfe39SJames Feist                 {
39850fdfe39SJames Feist                     continue;
39950fdfe39SJames Feist                 }
400*f3252315SPatrick Venture                 struct ControllerInfo& info =
40150fdfe39SJames Feist                     conf[variant_ns::get<std::string>(base.at("Name"))];
40250fdfe39SJames Feist                 info.inputs = std::move(inputs);
40350fdfe39SJames Feist 
40450fdfe39SJames Feist                 info.type = variant_ns::get<std::string>(base.at("Class"));
4058c3c51eeSJames Feist                 // todo: auto generation yaml -> c script seems to discard this
4068c3c51eeSJames Feist                 // value for fans, verify this is okay
4077136a5aeSJames Feist                 if (info.type == "fan")
4087136a5aeSJames Feist                 {
4097136a5aeSJames Feist                     info.setpoint = 0;
4107136a5aeSJames Feist                 }
4117136a5aeSJames Feist                 else
4127136a5aeSJames Feist                 {
413d7a55bfdSJames Feist                     info.setpoint = variant_ns::apply_visitor(
4148c3c51eeSJames Feist                         VariantToFloatVisitor(), base.at("SetPoint"));
4157136a5aeSJames Feist                 }
41622c257abSJames Feist                 info.pidInfo.ts = 1.0; // currently unused
417d7a55bfdSJames Feist                 info.pidInfo.p_c = variant_ns::apply_visitor(
4188c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("PCoefficient"));
419d7a55bfdSJames Feist                 info.pidInfo.i_c = variant_ns::apply_visitor(
4208c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("ICoefficient"));
421d7a55bfdSJames Feist                 info.pidInfo.ff_off = variant_ns::apply_visitor(
4227136a5aeSJames Feist                     VariantToFloatVisitor(), base.at("FFOffCoefficient"));
423d7a55bfdSJames Feist                 info.pidInfo.ff_gain = variant_ns::apply_visitor(
4247136a5aeSJames Feist                     VariantToFloatVisitor(), base.at("FFGainCoefficient"));
425d7a55bfdSJames Feist                 info.pidInfo.i_lim.max = variant_ns::apply_visitor(
4268c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("ILimitMax"));
427d7a55bfdSJames Feist                 info.pidInfo.i_lim.min = variant_ns::apply_visitor(
4288c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("ILimitMin"));
429d7a55bfdSJames Feist                 info.pidInfo.out_lim.max = variant_ns::apply_visitor(
4308c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("OutLimitMax"));
431d7a55bfdSJames Feist                 info.pidInfo.out_lim.min = variant_ns::apply_visitor(
4328c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("OutLimitMin"));
433d7a55bfdSJames Feist                 info.pidInfo.slew_neg = variant_ns::apply_visitor(
4347136a5aeSJames Feist                     VariantToFloatVisitor(), base.at("SlewNeg"));
435d7a55bfdSJames Feist                 info.pidInfo.slew_pos = variant_ns::apply_visitor(
4367136a5aeSJames Feist                     VariantToFloatVisitor(), base.at("SlewPos"));
4377136a5aeSJames Feist             }
4388c3c51eeSJames Feist         }
43922c257abSJames Feist         auto findStepwise =
44022c257abSJames Feist             configuration.second.find(stepwiseConfigurationInterface);
44122c257abSJames Feist         if (findStepwise != configuration.second.end())
44222c257abSJames Feist         {
44322c257abSJames Feist             const auto& base = findStepwise->second;
44422c257abSJames Feist             const std::vector<std::string>& zones =
44550fdfe39SJames Feist                 variant_ns::get<std::vector<std::string>>(base.at("Zones"));
44622c257abSJames Feist             for (const std::string& zone : zones)
44722c257abSJames Feist             {
44822c257abSJames Feist                 auto it = std::find(zoneIndex.begin(), zoneIndex.end(), zone);
44922c257abSJames Feist                 size_t index = 1;
45022c257abSJames Feist                 if (it == zoneIndex.end())
45122c257abSJames Feist                 {
45222c257abSJames Feist                     zoneIndex.emplace_back(zone);
45322c257abSJames Feist                     index = zoneIndex.size();
45422c257abSJames Feist                 }
45522c257abSJames Feist                 else
45622c257abSJames Feist                 {
45722c257abSJames Feist                     index = zoneIndex.end() - it;
45822c257abSJames Feist                 }
45922c257abSJames Feist                 PIDConf& conf = ZoneConfig[index];
46050fdfe39SJames Feist 
46150fdfe39SJames Feist                 std::vector<std::string> inputs;
46250fdfe39SJames Feist                 std::vector<std::string> sensorNames =
46350fdfe39SJames Feist                     variant_ns::get<std::vector<std::string>>(
46450fdfe39SJames Feist                         base.at("Inputs"));
46550fdfe39SJames Feist 
46650fdfe39SJames Feist                 bool sensorFound = sensorNames.size();
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(), ' ', '_');
47250fdfe39SJames Feist                     std::pair<std::string, std::string> sensorPathIfacePair;
47350fdfe39SJames Feist 
47450fdfe39SJames Feist                     if (!findSensor(sensors, name, sensorPathIfacePair))
47550fdfe39SJames Feist                     {
47650fdfe39SJames Feist                         sensorFound = false;
47750fdfe39SJames Feist                         break;
47850fdfe39SJames Feist                     }
47950fdfe39SJames Feist 
48050fdfe39SJames Feist                     inputs.push_back(name);
48150fdfe39SJames Feist                     auto& config = SensorConfig[name];
48250fdfe39SJames Feist                     config.readpath = sensorPathIfacePair.first;
48350fdfe39SJames Feist                     config.type = "temp";
48450fdfe39SJames Feist                     // todo: maybe un-hardcode this if we run into slower
48550fdfe39SJames Feist                     // timeouts with sensors
48650fdfe39SJames Feist 
48750fdfe39SJames Feist                     config.timeout = 500;
48850fdfe39SJames Feist                 }
48950fdfe39SJames Feist                 if (!sensorFound)
49050fdfe39SJames Feist                 {
49150fdfe39SJames Feist                     continue;
49250fdfe39SJames Feist                 }
493*f3252315SPatrick Venture                 struct ControllerInfo& info =
49450fdfe39SJames Feist                     conf[variant_ns::get<std::string>(base.at("Name"))];
49550fdfe39SJames Feist                 info.inputs = std::move(inputs);
49650fdfe39SJames Feist 
49722c257abSJames Feist                 info.type = "stepwise";
49822c257abSJames Feist                 info.stepwiseInfo.ts = 1.0; // currently unused
4993dfaafdaSJames Feist                 info.stepwiseInfo.positiveHysteresis = 0.0;
5003dfaafdaSJames Feist                 info.stepwiseInfo.negativeHysteresis = 0.0;
5013dfaafdaSJames Feist                 auto findPosHyst = base.find("PositiveHysteresis");
5023dfaafdaSJames Feist                 auto findNegHyst = base.find("NegativeHysteresis");
5033dfaafdaSJames Feist                 if (findPosHyst != base.end())
5043dfaafdaSJames Feist                 {
5053dfaafdaSJames Feist                     info.stepwiseInfo.positiveHysteresis =
506d7a55bfdSJames Feist                         variant_ns::apply_visitor(VariantToFloatVisitor(),
5073dfaafdaSJames Feist                                                   findPosHyst->second);
5083dfaafdaSJames Feist                 }
5093dfaafdaSJames Feist                 if (findNegHyst != base.end())
5103dfaafdaSJames Feist                 {
5113dfaafdaSJames Feist                     info.stepwiseInfo.positiveHysteresis =
512d7a55bfdSJames Feist                         variant_ns::apply_visitor(VariantToFloatVisitor(),
5133dfaafdaSJames Feist                                                   findNegHyst->second);
5143dfaafdaSJames Feist                 }
51522c257abSJames Feist                 std::vector<double> readings =
51650fdfe39SJames Feist                     variant_ns::get<std::vector<double>>(base.at("Reading"));
51722c257abSJames Feist                 if (readings.size() > ec::maxStepwisePoints)
51822c257abSJames Feist                 {
51922c257abSJames Feist                     throw std::invalid_argument("Too many stepwise points.");
52022c257abSJames Feist                 }
52122c257abSJames Feist                 if (readings.empty())
52222c257abSJames Feist                 {
52322c257abSJames Feist                     throw std::invalid_argument(
52422c257abSJames Feist                         "Must have one stepwise point.");
52522c257abSJames Feist                 }
52622c257abSJames Feist                 std::copy(readings.begin(), readings.end(),
52722c257abSJames Feist                           info.stepwiseInfo.reading);
52822c257abSJames Feist                 if (readings.size() < ec::maxStepwisePoints)
52922c257abSJames Feist                 {
53022c257abSJames Feist                     info.stepwiseInfo.reading[readings.size()] =
53122c257abSJames Feist                         std::numeric_limits<float>::quiet_NaN();
53222c257abSJames Feist                 }
53322c257abSJames Feist                 std::vector<double> outputs =
53450fdfe39SJames Feist                     variant_ns::get<std::vector<double>>(base.at("Output"));
53522c257abSJames Feist                 if (readings.size() != outputs.size())
53622c257abSJames Feist                 {
53722c257abSJames Feist                     throw std::invalid_argument(
53822c257abSJames Feist                         "Outputs size must match readings");
53922c257abSJames Feist                 }
54022c257abSJames Feist                 std::copy(outputs.begin(), outputs.end(),
54122c257abSJames Feist                           info.stepwiseInfo.output);
54222c257abSJames Feist                 if (outputs.size() < ec::maxStepwisePoints)
54322c257abSJames Feist                 {
54422c257abSJames Feist                     info.stepwiseInfo.output[outputs.size()] =
54522c257abSJames Feist                         std::numeric_limits<float>::quiet_NaN();
54622c257abSJames Feist                 }
54722c257abSJames Feist             }
54822c257abSJames Feist         }
54922c257abSJames Feist     }
5507136a5aeSJames Feist     if (DEBUG)
5517136a5aeSJames Feist     {
5527136a5aeSJames Feist         debugPrint();
5537136a5aeSJames Feist     }
55450fdfe39SJames Feist     if (ZoneConfig.empty())
55550fdfe39SJames Feist     {
55650fdfe39SJames Feist         std::cerr << "No fan zones, application pausing until reboot\n";
55750fdfe39SJames Feist         while (1)
55850fdfe39SJames Feist         {
55950fdfe39SJames Feist             bus.process_discard();
56065ea92e7SJames Feist             bus.wait();
56150fdfe39SJames Feist         }
56250fdfe39SJames Feist     }
5637136a5aeSJames Feist }
5647136a5aeSJames Feist } // namespace dbus_configuration
565