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 
1764f072a7SJames Feist #include <chrono>
187136a5aeSJames Feist #include <conf.hpp>
197136a5aeSJames Feist #include <dbus/util.hpp>
2064f072a7SJames Feist #include <functional>
217136a5aeSJames Feist #include <iostream>
227136a5aeSJames Feist #include <sdbusplus/bus.hpp>
2364f072a7SJames Feist #include <sdbusplus/bus/match.hpp>
2422c257abSJames Feist #include <sdbusplus/exception.hpp>
257136a5aeSJames Feist #include <set>
2664f072a7SJames Feist #include <thread>
277136a5aeSJames Feist #include <unordered_map>
287136a5aeSJames Feist 
297136a5aeSJames Feist static constexpr bool DEBUG = false; // enable to print found configuration
307136a5aeSJames Feist 
317136a5aeSJames Feist std::map<std::string, struct sensor> SensorConfig = {};
327136a5aeSJames Feist std::map<int64_t, PIDConf> ZoneConfig = {};
337136a5aeSJames Feist std::map<int64_t, struct zone> ZoneDetailsConfig = {};
347136a5aeSJames Feist 
357136a5aeSJames Feist constexpr const char* pidConfigurationInterface =
367136a5aeSJames Feist     "xyz.openbmc_project.Configuration.Pid";
377136a5aeSJames Feist constexpr const char* objectManagerInterface =
387136a5aeSJames Feist     "org.freedesktop.DBus.ObjectManager";
397136a5aeSJames Feist constexpr const char* pidZoneConfigurationInterface =
407136a5aeSJames Feist     "xyz.openbmc_project.Configuration.Pid.Zone";
4122c257abSJames Feist constexpr const char* stepwiseConfigurationInterface =
4222c257abSJames Feist     "xyz.openbmc_project.Configuration.Stepwise";
437136a5aeSJames Feist constexpr const char* sensorInterface = "xyz.openbmc_project.Sensor.Value";
447136a5aeSJames Feist constexpr const char* pwmInterface = "xyz.openbmc_project.Control.FanPwm";
457136a5aeSJames Feist 
467136a5aeSJames Feist namespace dbus_configuration
477136a5aeSJames Feist {
487136a5aeSJames Feist 
4950fdfe39SJames Feist namespace variant_ns = sdbusplus::message::variant_ns;
5050fdfe39SJames Feist 
517136a5aeSJames Feist bool findSensor(const std::unordered_map<std::string, std::string>& sensors,
527136a5aeSJames Feist                 const std::string& search,
537136a5aeSJames Feist                 std::pair<std::string, std::string>& sensor)
547136a5aeSJames Feist {
557136a5aeSJames Feist     for (const auto& s : sensors)
567136a5aeSJames Feist     {
577136a5aeSJames Feist         if (s.first.find(search) != std::string::npos)
587136a5aeSJames Feist         {
597136a5aeSJames Feist             sensor = s;
607136a5aeSJames Feist             return true;
617136a5aeSJames Feist         }
627136a5aeSJames Feist     }
637136a5aeSJames Feist     return false;
647136a5aeSJames Feist }
657136a5aeSJames Feist 
667136a5aeSJames Feist // this function prints the configuration into a form similar to the cpp
677136a5aeSJames Feist // generated code to help in verification, should be turned off during normal
687136a5aeSJames Feist // use
697136a5aeSJames Feist void debugPrint(void)
707136a5aeSJames Feist {
717136a5aeSJames Feist     // print sensor config
727136a5aeSJames Feist     std::cout << "sensor config:\n";
737136a5aeSJames Feist     std::cout << "{\n";
747136a5aeSJames Feist     for (auto& pair : SensorConfig)
757136a5aeSJames Feist     {
767136a5aeSJames Feist 
777136a5aeSJames Feist         std::cout << "\t{" << pair.first << ",\n\t\t{";
787136a5aeSJames Feist         std::cout << pair.second.type << ", ";
797136a5aeSJames Feist         std::cout << pair.second.readpath << ", ";
807136a5aeSJames Feist         std::cout << pair.second.writepath << ", ";
817136a5aeSJames Feist         std::cout << pair.second.min << ", ";
827136a5aeSJames Feist         std::cout << pair.second.max << ", ";
837136a5aeSJames Feist         std::cout << pair.second.timeout << "},\n\t},\n";
847136a5aeSJames Feist     }
857136a5aeSJames Feist     std::cout << "}\n\n";
867136a5aeSJames Feist     std::cout << "ZoneDetailsConfig\n";
877136a5aeSJames Feist     std::cout << "{\n";
887136a5aeSJames Feist     for (auto& zone : ZoneDetailsConfig)
897136a5aeSJames Feist     {
907136a5aeSJames Feist         std::cout << "\t{" << zone.first << ",\n";
917136a5aeSJames Feist         std::cout << "\t\t{" << zone.second.minthermalrpm << ", ";
927136a5aeSJames Feist         std::cout << zone.second.failsafepercent << "}\n\t},\n";
937136a5aeSJames Feist     }
947136a5aeSJames Feist     std::cout << "}\n\n";
957136a5aeSJames Feist     std::cout << "ZoneConfig\n";
967136a5aeSJames Feist     std::cout << "{\n";
977136a5aeSJames Feist     for (auto& zone : ZoneConfig)
987136a5aeSJames Feist     {
997136a5aeSJames Feist         std::cout << "\t{" << zone.first << "\n";
1007136a5aeSJames Feist         for (auto& pidconf : zone.second)
1017136a5aeSJames Feist         {
1027136a5aeSJames Feist             std::cout << "\t\t{" << pidconf.first << ",\n";
1037136a5aeSJames Feist             std::cout << "\t\t\t{" << pidconf.second.type << ",\n";
1047136a5aeSJames Feist             std::cout << "\t\t\t{";
1057136a5aeSJames Feist             for (auto& input : pidconf.second.inputs)
1067136a5aeSJames Feist             {
1077136a5aeSJames Feist                 std::cout << "\n\t\t\t" << input << ",\n";
1087136a5aeSJames Feist             }
1097136a5aeSJames Feist             std::cout << "\t\t\t}\n";
1107136a5aeSJames Feist             std::cout << "\t\t\t" << pidconf.second.setpoint << ",\n";
11122c257abSJames Feist             std::cout << "\t\t\t{" << pidconf.second.pidInfo.ts << ",\n";
11222c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.p_c << ",\n";
11322c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.i_c << ",\n";
11422c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.ff_off << ",\n";
11522c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.ff_gain << ",\n";
11622c257abSJames Feist             std::cout << "\t\t\t{" << pidconf.second.pidInfo.i_lim.min << ","
11722c257abSJames Feist                       << pidconf.second.pidInfo.i_lim.max << "},\n";
11822c257abSJames Feist             std::cout << "\t\t\t{" << pidconf.second.pidInfo.out_lim.min << ","
11922c257abSJames Feist                       << pidconf.second.pidInfo.out_lim.max << "},\n";
12022c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.slew_neg << ",\n";
12122c257abSJames Feist             std::cout << "\t\t\t" << pidconf.second.pidInfo.slew_pos << ",\n";
1227136a5aeSJames Feist             std::cout << "\t\t\t}\n\t\t}\n";
1237136a5aeSJames Feist         }
1247136a5aeSJames Feist         std::cout << "\t},\n";
1257136a5aeSJames Feist     }
1267136a5aeSJames Feist     std::cout << "}\n\n";
1277136a5aeSJames Feist }
1287136a5aeSJames Feist 
12950fdfe39SJames Feist int eventHandler(sd_bus_message*, void*, sd_bus_error*)
13050fdfe39SJames Feist {
13150fdfe39SJames Feist     // do a brief sleep as we tend to get a bunch of these events at
13250fdfe39SJames Feist     // once
13350fdfe39SJames Feist     std::this_thread::sleep_for(std::chrono::seconds(5));
13450fdfe39SJames Feist     std::cout << "New configuration detected, restarting\n.";
13550fdfe39SJames Feist     std::exit(EXIT_SUCCESS); // service file should make us restart
13650fdfe39SJames Feist     return 1;
13750fdfe39SJames Feist }
13850fdfe39SJames Feist 
1397136a5aeSJames Feist void init(sdbusplus::bus::bus& bus)
1407136a5aeSJames Feist {
14122c257abSJames Feist     using DbusVariantType =
14222c257abSJames Feist         sdbusplus::message::variant<uint64_t, int64_t, double, std::string,
14322c257abSJames Feist                                     std::vector<std::string>,
14422c257abSJames Feist                                     std::vector<double>>;
14522c257abSJames Feist 
1467136a5aeSJames Feist     using ManagedObjectType = std::unordered_map<
1477136a5aeSJames Feist         sdbusplus::message::object_path,
1487136a5aeSJames Feist         std::unordered_map<std::string,
14922c257abSJames Feist                            std::unordered_map<std::string, DbusVariantType>>>;
15064f072a7SJames Feist 
15150fdfe39SJames Feist     // restart on configuration properties changed
15250fdfe39SJames Feist     static sdbusplus::bus::match::match configMatch(
15364f072a7SJames Feist         bus,
15464f072a7SJames Feist         "type='signal',member='PropertiesChanged',arg0namespace='" +
15564f072a7SJames Feist             std::string(pidConfigurationInterface) + "'",
15664f072a7SJames Feist         eventHandler);
15764f072a7SJames Feist 
15850fdfe39SJames Feist     // restart on sensors changed
15950fdfe39SJames Feist     static sdbusplus::bus::match::match sensorAdded(
16050fdfe39SJames Feist         bus,
16150fdfe39SJames Feist         "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
16250fdfe39SJames Feist         "sensors/'",
16350fdfe39SJames Feist         eventHandler);
16450fdfe39SJames Feist 
1657136a5aeSJames Feist     auto mapper =
1667136a5aeSJames Feist         bus.new_method_call("xyz.openbmc_project.ObjectMapper",
1677136a5aeSJames Feist                             "/xyz/openbmc_project/object_mapper",
1687136a5aeSJames Feist                             "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1697136a5aeSJames Feist     mapper.append("", 0,
17022c257abSJames Feist                   std::array<const char*, 6>{objectManagerInterface,
1717136a5aeSJames Feist                                              pidConfigurationInterface,
1727136a5aeSJames Feist                                              pidZoneConfigurationInterface,
17322c257abSJames Feist                                              stepwiseConfigurationInterface,
1747136a5aeSJames Feist                                              sensorInterface, pwmInterface});
17522c257abSJames Feist     std::unordered_map<
17622c257abSJames Feist         std::string, std::unordered_map<std::string, std::vector<std::string>>>
17722c257abSJames Feist         respData;
17822c257abSJames Feist     try
17922c257abSJames Feist     {
1807136a5aeSJames Feist         auto resp = bus.call(mapper);
1817136a5aeSJames Feist         if (resp.is_method_error())
1827136a5aeSJames Feist         {
1837136a5aeSJames Feist             throw std::runtime_error("ObjectMapper Call Failure");
1847136a5aeSJames Feist         }
1857136a5aeSJames Feist         resp.read(respData);
18622c257abSJames Feist     }
18722c257abSJames Feist     catch (sdbusplus::exception_t&)
18822c257abSJames Feist     {
18922c257abSJames Feist         // can't do anything without mapper call data
19022c257abSJames Feist         throw std::runtime_error("ObjectMapper Call Failure");
19122c257abSJames Feist     }
19222c257abSJames Feist 
1937136a5aeSJames Feist     if (respData.empty())
1947136a5aeSJames Feist     {
19522c257abSJames Feist         // can't do anything without mapper call data
1967136a5aeSJames Feist         throw std::runtime_error("No configuration data available from Mapper");
1977136a5aeSJames Feist     }
1987136a5aeSJames Feist     // create a map of pair of <has pid configuration, ObjectManager path>
1997136a5aeSJames Feist     std::unordered_map<std::string, std::pair<bool, std::string>> owners;
2007136a5aeSJames Feist     // and a map of <path, interface> for sensors
2017136a5aeSJames Feist     std::unordered_map<std::string, std::string> sensors;
2027136a5aeSJames Feist     for (const auto& objectPair : respData)
2037136a5aeSJames Feist     {
2047136a5aeSJames Feist         for (const auto& ownerPair : objectPair.second)
2057136a5aeSJames Feist         {
2067136a5aeSJames Feist             auto& owner = owners[ownerPair.first];
2077136a5aeSJames Feist             for (const std::string& interface : ownerPair.second)
2087136a5aeSJames Feist             {
2097136a5aeSJames Feist 
2107136a5aeSJames Feist                 if (interface == objectManagerInterface)
2117136a5aeSJames Feist                 {
2127136a5aeSJames Feist                     owner.second = objectPair.first;
2137136a5aeSJames Feist                 }
2147136a5aeSJames Feist                 if (interface == pidConfigurationInterface ||
21522c257abSJames Feist                     interface == pidZoneConfigurationInterface ||
21622c257abSJames Feist                     interface == stepwiseConfigurationInterface)
2177136a5aeSJames Feist                 {
2187136a5aeSJames Feist                     owner.first = true;
2197136a5aeSJames Feist                 }
2207136a5aeSJames Feist                 if (interface == sensorInterface || interface == pwmInterface)
2217136a5aeSJames Feist                 {
2227136a5aeSJames Feist                     // we're not interested in pwm sensors, just pwm control
2237136a5aeSJames Feist                     if (interface == sensorInterface &&
2247136a5aeSJames Feist                         objectPair.first.find("pwm") != std::string::npos)
2257136a5aeSJames Feist                     {
2267136a5aeSJames Feist                         continue;
2277136a5aeSJames Feist                     }
2287136a5aeSJames Feist                     sensors[objectPair.first] = interface;
2297136a5aeSJames Feist                 }
2307136a5aeSJames Feist             }
2317136a5aeSJames Feist         }
2327136a5aeSJames Feist     }
2337136a5aeSJames Feist     ManagedObjectType configurations;
2347136a5aeSJames Feist     for (const auto& owner : owners)
2357136a5aeSJames Feist     {
2367136a5aeSJames Feist         // skip if no pid configuration (means probably a sensor)
2377136a5aeSJames Feist         if (!owner.second.first)
2387136a5aeSJames Feist         {
2397136a5aeSJames Feist             continue;
2407136a5aeSJames Feist         }
2417136a5aeSJames Feist         auto endpoint = bus.new_method_call(
2427136a5aeSJames Feist             owner.first.c_str(), owner.second.second.c_str(),
2437136a5aeSJames Feist             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
24422c257abSJames Feist         ManagedObjectType configuration;
24522c257abSJames Feist         try
24622c257abSJames Feist         {
2477136a5aeSJames Feist             auto responce = bus.call(endpoint);
2487136a5aeSJames Feist             if (responce.is_method_error())
2497136a5aeSJames Feist             {
2507136a5aeSJames Feist                 throw std::runtime_error("Error getting managed objects from " +
2517136a5aeSJames Feist                                          owner.first);
2527136a5aeSJames Feist             }
2537136a5aeSJames Feist             responce.read(configuration);
25422c257abSJames Feist         }
25522c257abSJames Feist         catch (sdbusplus::exception_t&)
25622c257abSJames Feist         {
25722c257abSJames Feist             // this shouldn't happen, probably means daemon crashed
25822c257abSJames Feist             throw std::runtime_error("Error getting managed objects from " +
25922c257abSJames Feist                                      owner.first);
26022c257abSJames Feist         }
26122c257abSJames Feist 
2627136a5aeSJames Feist         for (auto& pathPair : configuration)
2637136a5aeSJames Feist         {
2647136a5aeSJames Feist             if (pathPair.second.find(pidConfigurationInterface) !=
2657136a5aeSJames Feist                     pathPair.second.end() ||
2667136a5aeSJames Feist                 pathPair.second.find(pidZoneConfigurationInterface) !=
26722c257abSJames Feist                     pathPair.second.end() ||
26822c257abSJames Feist                 pathPair.second.find(stepwiseConfigurationInterface) !=
2697136a5aeSJames Feist                     pathPair.second.end())
2707136a5aeSJames Feist             {
2717136a5aeSJames Feist                 configurations.emplace(pathPair);
2727136a5aeSJames Feist             }
2737136a5aeSJames Feist         }
2747136a5aeSJames Feist     }
2758c3c51eeSJames Feist 
2768c3c51eeSJames Feist     // on dbus having an index field is a bit strange, so randomly
2778c3c51eeSJames Feist     // assign index based on name property
2788c3c51eeSJames Feist     std::vector<std::string> zoneIndex;
2797136a5aeSJames Feist     for (const auto& configuration : configurations)
2807136a5aeSJames Feist     {
2817136a5aeSJames Feist         auto findZone =
2827136a5aeSJames Feist             configuration.second.find(pidZoneConfigurationInterface);
2837136a5aeSJames Feist         if (findZone != configuration.second.end())
2847136a5aeSJames Feist         {
2857136a5aeSJames Feist             const auto& zone = findZone->second;
2868c3c51eeSJames Feist             size_t index = 1;
2878c3c51eeSJames Feist             const std::string& name =
28850fdfe39SJames Feist                 variant_ns::get<std::string>(zone.at("Name"));
2898c3c51eeSJames Feist             auto it = std::find(zoneIndex.begin(), zoneIndex.end(), name);
2908c3c51eeSJames Feist             if (it == zoneIndex.end())
2918c3c51eeSJames Feist             {
2928c3c51eeSJames Feist                 zoneIndex.emplace_back(name);
2938c3c51eeSJames Feist                 index = zoneIndex.size();
2948c3c51eeSJames Feist             }
2958c3c51eeSJames Feist             else
2968c3c51eeSJames Feist             {
2978c3c51eeSJames Feist                 index = zoneIndex.end() - it;
2988c3c51eeSJames Feist             }
2998c3c51eeSJames Feist 
3008c3c51eeSJames Feist             auto& details = ZoneDetailsConfig[index];
301*d7a55bfdSJames Feist             details.minthermalrpm = variant_ns::apply_visitor(
3027136a5aeSJames Feist                 VariantToFloatVisitor(), zone.at("MinThermalRpm"));
303*d7a55bfdSJames Feist             details.failsafepercent = variant_ns::apply_visitor(
3047136a5aeSJames Feist                 VariantToFloatVisitor(), zone.at("FailSafePercent"));
3057136a5aeSJames Feist         }
3067136a5aeSJames Feist         auto findBase = configuration.second.find(pidConfigurationInterface);
30722c257abSJames Feist         if (findBase != configuration.second.end())
3087136a5aeSJames Feist         {
3098c3c51eeSJames Feist 
31022c257abSJames Feist             const auto& base =
31122c257abSJames Feist                 configuration.second.at(pidConfigurationInterface);
3128c3c51eeSJames Feist             const std::vector<std::string>& zones =
31350fdfe39SJames Feist                 variant_ns::get<std::vector<std::string>>(base.at("Zones"));
3148c3c51eeSJames Feist             for (const std::string& zone : zones)
3158c3c51eeSJames Feist             {
3168c3c51eeSJames Feist                 auto it = std::find(zoneIndex.begin(), zoneIndex.end(), zone);
3178c3c51eeSJames Feist                 size_t index = 1;
3188c3c51eeSJames Feist                 if (it == zoneIndex.end())
3198c3c51eeSJames Feist                 {
3208c3c51eeSJames Feist                     zoneIndex.emplace_back(zone);
3218c3c51eeSJames Feist                     index = zoneIndex.size();
3228c3c51eeSJames Feist                 }
3238c3c51eeSJames Feist                 else
3248c3c51eeSJames Feist                 {
3258c3c51eeSJames Feist                     index = zoneIndex.end() - it;
3268c3c51eeSJames Feist                 }
3278c3c51eeSJames Feist                 PIDConf& conf = ZoneConfig[index];
32850fdfe39SJames Feist 
32950fdfe39SJames Feist                 std::vector<std::string> sensorNames =
33050fdfe39SJames Feist                     variant_ns::get<std::vector<std::string>>(
33150fdfe39SJames Feist                         base.at("Inputs"));
33250fdfe39SJames Feist                 auto findOutputs =
33350fdfe39SJames Feist                     base.find("Outputs"); // currently only fans have outputs
33450fdfe39SJames Feist                 if (findOutputs != base.end())
33550fdfe39SJames Feist                 {
33650fdfe39SJames Feist                     std::vector<std::string> outputs =
33750fdfe39SJames Feist                         variant_ns::get<std::vector<std::string>>(
33850fdfe39SJames Feist                             findOutputs->second);
33950fdfe39SJames Feist                     sensorNames.insert(sensorNames.end(), outputs.begin(),
34050fdfe39SJames Feist                                        outputs.end());
34150fdfe39SJames Feist                 }
34250fdfe39SJames Feist                 bool sensorsAvailable = sensorNames.size();
34350fdfe39SJames Feist                 std::vector<std::string> inputs;
34450fdfe39SJames Feist                 for (const std::string& sensorName : sensorNames)
34550fdfe39SJames Feist                 {
34650fdfe39SJames Feist                     std::string name = sensorName;
34750fdfe39SJames Feist                     // replace spaces with underscores to be legal on dbus
34850fdfe39SJames Feist                     std::replace(name.begin(), name.end(), ' ', '_');
34950fdfe39SJames Feist                     std::pair<std::string, std::string> sensorPathIfacePair;
35050fdfe39SJames Feist 
35150fdfe39SJames Feist                     if (!findSensor(sensors, name, sensorPathIfacePair))
35250fdfe39SJames Feist                     {
35350fdfe39SJames Feist                         sensorsAvailable = false;
35450fdfe39SJames Feist                         break;
35550fdfe39SJames Feist                     }
35650fdfe39SJames Feist                     if (sensorPathIfacePair.second == sensorInterface)
35750fdfe39SJames Feist                     {
35850fdfe39SJames Feist                         inputs.push_back(name);
35950fdfe39SJames Feist                         auto& config = SensorConfig[name];
36050fdfe39SJames Feist                         config.type =
36150fdfe39SJames Feist                             variant_ns::get<std::string>(base.at("Class"));
36250fdfe39SJames Feist                         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                         {
36750fdfe39SJames Feist                             config.timeout = 500;
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                         {
37550fdfe39SJames Feist                             if (otherSensor == sensorName)
37650fdfe39SJames Feist                             {
37750fdfe39SJames Feist                                 continue;
37850fdfe39SJames Feist                             }
37950fdfe39SJames Feist                             std::replace(otherSensor.begin(), otherSensor.end(),
38050fdfe39SJames Feist                                          ' ', '_');
38150fdfe39SJames Feist                             auto& config = SensorConfig[otherSensor];
38250fdfe39SJames Feist                             config.writepath = sensorPathIfacePair.first;
38350fdfe39SJames Feist                             // todo: un-hardcode this if there are fans with
38450fdfe39SJames Feist                             // different ranges
38550fdfe39SJames Feist                             config.max = 255;
38650fdfe39SJames Feist                             config.min = 0;
38750fdfe39SJames Feist                         }
38850fdfe39SJames Feist                     }
38950fdfe39SJames Feist                 }
39050fdfe39SJames Feist                 // if the sensors aren't available in the current state, don't
39150fdfe39SJames Feist                 // add them to the configuration.
39250fdfe39SJames Feist                 if (!sensorsAvailable)
39350fdfe39SJames Feist                 {
39450fdfe39SJames Feist                     continue;
39550fdfe39SJames Feist                 }
3967136a5aeSJames Feist                 struct controller_info& info =
39750fdfe39SJames Feist                     conf[variant_ns::get<std::string>(base.at("Name"))];
39850fdfe39SJames Feist                 info.inputs = std::move(inputs);
39950fdfe39SJames Feist 
40050fdfe39SJames Feist                 info.type = variant_ns::get<std::string>(base.at("Class"));
4018c3c51eeSJames Feist                 // todo: auto generation yaml -> c script seems to discard this
4028c3c51eeSJames Feist                 // value for fans, verify this is okay
4037136a5aeSJames Feist                 if (info.type == "fan")
4047136a5aeSJames Feist                 {
4057136a5aeSJames Feist                     info.setpoint = 0;
4067136a5aeSJames Feist                 }
4077136a5aeSJames Feist                 else
4087136a5aeSJames Feist                 {
409*d7a55bfdSJames Feist                     info.setpoint = variant_ns::apply_visitor(
4108c3c51eeSJames Feist                         VariantToFloatVisitor(), base.at("SetPoint"));
4117136a5aeSJames Feist                 }
41222c257abSJames Feist                 info.pidInfo.ts = 1.0; // currently unused
413*d7a55bfdSJames Feist                 info.pidInfo.p_c = variant_ns::apply_visitor(
4148c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("PCoefficient"));
415*d7a55bfdSJames Feist                 info.pidInfo.i_c = variant_ns::apply_visitor(
4168c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("ICoefficient"));
417*d7a55bfdSJames Feist                 info.pidInfo.ff_off = variant_ns::apply_visitor(
4187136a5aeSJames Feist                     VariantToFloatVisitor(), base.at("FFOffCoefficient"));
419*d7a55bfdSJames Feist                 info.pidInfo.ff_gain = variant_ns::apply_visitor(
4207136a5aeSJames Feist                     VariantToFloatVisitor(), base.at("FFGainCoefficient"));
421*d7a55bfdSJames Feist                 info.pidInfo.i_lim.max = variant_ns::apply_visitor(
4228c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("ILimitMax"));
423*d7a55bfdSJames Feist                 info.pidInfo.i_lim.min = variant_ns::apply_visitor(
4248c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("ILimitMin"));
425*d7a55bfdSJames Feist                 info.pidInfo.out_lim.max = variant_ns::apply_visitor(
4268c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("OutLimitMax"));
427*d7a55bfdSJames Feist                 info.pidInfo.out_lim.min = variant_ns::apply_visitor(
4288c3c51eeSJames Feist                     VariantToFloatVisitor(), base.at("OutLimitMin"));
429*d7a55bfdSJames Feist                 info.pidInfo.slew_neg = variant_ns::apply_visitor(
4307136a5aeSJames Feist                     VariantToFloatVisitor(), base.at("SlewNeg"));
431*d7a55bfdSJames Feist                 info.pidInfo.slew_pos = variant_ns::apply_visitor(
4327136a5aeSJames Feist                     VariantToFloatVisitor(), base.at("SlewPos"));
4337136a5aeSJames Feist             }
4348c3c51eeSJames Feist         }
43522c257abSJames Feist         auto findStepwise =
43622c257abSJames Feist             configuration.second.find(stepwiseConfigurationInterface);
43722c257abSJames Feist         if (findStepwise != configuration.second.end())
43822c257abSJames Feist         {
43922c257abSJames Feist             const auto& base = findStepwise->second;
44022c257abSJames Feist             const std::vector<std::string>& zones =
44150fdfe39SJames Feist                 variant_ns::get<std::vector<std::string>>(base.at("Zones"));
44222c257abSJames Feist             for (const std::string& zone : zones)
44322c257abSJames Feist             {
44422c257abSJames Feist                 auto it = std::find(zoneIndex.begin(), zoneIndex.end(), zone);
44522c257abSJames Feist                 size_t index = 1;
44622c257abSJames Feist                 if (it == zoneIndex.end())
44722c257abSJames Feist                 {
44822c257abSJames Feist                     zoneIndex.emplace_back(zone);
44922c257abSJames Feist                     index = zoneIndex.size();
45022c257abSJames Feist                 }
45122c257abSJames Feist                 else
45222c257abSJames Feist                 {
45322c257abSJames Feist                     index = zoneIndex.end() - it;
45422c257abSJames Feist                 }
45522c257abSJames Feist                 PIDConf& conf = ZoneConfig[index];
45650fdfe39SJames Feist 
45750fdfe39SJames Feist                 std::vector<std::string> inputs;
45850fdfe39SJames Feist                 std::vector<std::string> sensorNames =
45950fdfe39SJames Feist                     variant_ns::get<std::vector<std::string>>(
46050fdfe39SJames Feist                         base.at("Inputs"));
46150fdfe39SJames Feist 
46250fdfe39SJames Feist                 bool sensorFound = sensorNames.size();
46350fdfe39SJames Feist                 for (const std::string& sensorName : sensorNames)
46450fdfe39SJames Feist                 {
46550fdfe39SJames Feist                     std::string name = sensorName;
46650fdfe39SJames Feist                     // replace spaces with underscores to be legal on dbus
46750fdfe39SJames Feist                     std::replace(name.begin(), name.end(), ' ', '_');
46850fdfe39SJames Feist                     std::pair<std::string, std::string> sensorPathIfacePair;
46950fdfe39SJames Feist 
47050fdfe39SJames Feist                     if (!findSensor(sensors, name, sensorPathIfacePair))
47150fdfe39SJames Feist                     {
47250fdfe39SJames Feist                         sensorFound = false;
47350fdfe39SJames Feist                         break;
47450fdfe39SJames Feist                     }
47550fdfe39SJames Feist 
47650fdfe39SJames Feist                     inputs.push_back(name);
47750fdfe39SJames Feist                     auto& config = SensorConfig[name];
47850fdfe39SJames Feist                     config.readpath = sensorPathIfacePair.first;
47950fdfe39SJames Feist                     config.type = "temp";
48050fdfe39SJames Feist                     // todo: maybe un-hardcode this if we run into slower
48150fdfe39SJames Feist                     // timeouts with sensors
48250fdfe39SJames Feist 
48350fdfe39SJames Feist                     config.timeout = 500;
48450fdfe39SJames Feist                 }
48550fdfe39SJames Feist                 if (!sensorFound)
48650fdfe39SJames Feist                 {
48750fdfe39SJames Feist                     continue;
48850fdfe39SJames Feist                 }
48922c257abSJames Feist                 struct controller_info& info =
49050fdfe39SJames Feist                     conf[variant_ns::get<std::string>(base.at("Name"))];
49150fdfe39SJames Feist                 info.inputs = std::move(inputs);
49250fdfe39SJames Feist 
49322c257abSJames Feist                 info.type = "stepwise";
49422c257abSJames Feist                 info.stepwiseInfo.ts = 1.0; // currently unused
4953dfaafdaSJames Feist                 info.stepwiseInfo.positiveHysteresis = 0.0;
4963dfaafdaSJames Feist                 info.stepwiseInfo.negativeHysteresis = 0.0;
4973dfaafdaSJames Feist                 auto findPosHyst = base.find("PositiveHysteresis");
4983dfaafdaSJames Feist                 auto findNegHyst = base.find("NegativeHysteresis");
4993dfaafdaSJames Feist                 if (findPosHyst != base.end())
5003dfaafdaSJames Feist                 {
5013dfaafdaSJames Feist                     info.stepwiseInfo.positiveHysteresis =
502*d7a55bfdSJames Feist                         variant_ns::apply_visitor(VariantToFloatVisitor(),
5033dfaafdaSJames Feist                                                   findPosHyst->second);
5043dfaafdaSJames Feist                 }
5053dfaafdaSJames Feist                 if (findNegHyst != base.end())
5063dfaafdaSJames Feist                 {
5073dfaafdaSJames Feist                     info.stepwiseInfo.positiveHysteresis =
508*d7a55bfdSJames Feist                         variant_ns::apply_visitor(VariantToFloatVisitor(),
5093dfaafdaSJames Feist                                                   findNegHyst->second);
5103dfaafdaSJames Feist                 }
51122c257abSJames Feist                 std::vector<double> readings =
51250fdfe39SJames Feist                     variant_ns::get<std::vector<double>>(base.at("Reading"));
51322c257abSJames Feist                 if (readings.size() > ec::maxStepwisePoints)
51422c257abSJames Feist                 {
51522c257abSJames Feist                     throw std::invalid_argument("Too many stepwise points.");
51622c257abSJames Feist                 }
51722c257abSJames Feist                 if (readings.empty())
51822c257abSJames Feist                 {
51922c257abSJames Feist                     throw std::invalid_argument(
52022c257abSJames Feist                         "Must have one stepwise point.");
52122c257abSJames Feist                 }
52222c257abSJames Feist                 std::copy(readings.begin(), readings.end(),
52322c257abSJames Feist                           info.stepwiseInfo.reading);
52422c257abSJames Feist                 if (readings.size() < ec::maxStepwisePoints)
52522c257abSJames Feist                 {
52622c257abSJames Feist                     info.stepwiseInfo.reading[readings.size()] =
52722c257abSJames Feist                         std::numeric_limits<float>::quiet_NaN();
52822c257abSJames Feist                 }
52922c257abSJames Feist                 std::vector<double> outputs =
53050fdfe39SJames Feist                     variant_ns::get<std::vector<double>>(base.at("Output"));
53122c257abSJames Feist                 if (readings.size() != outputs.size())
53222c257abSJames Feist                 {
53322c257abSJames Feist                     throw std::invalid_argument(
53422c257abSJames Feist                         "Outputs size must match readings");
53522c257abSJames Feist                 }
53622c257abSJames Feist                 std::copy(outputs.begin(), outputs.end(),
53722c257abSJames Feist                           info.stepwiseInfo.output);
53822c257abSJames Feist                 if (outputs.size() < ec::maxStepwisePoints)
53922c257abSJames Feist                 {
54022c257abSJames Feist                     info.stepwiseInfo.output[outputs.size()] =
54122c257abSJames Feist                         std::numeric_limits<float>::quiet_NaN();
54222c257abSJames Feist                 }
54322c257abSJames Feist             }
54422c257abSJames Feist         }
54522c257abSJames Feist     }
5467136a5aeSJames Feist     if (DEBUG)
5477136a5aeSJames Feist     {
5487136a5aeSJames Feist         debugPrint();
5497136a5aeSJames Feist     }
55050fdfe39SJames Feist     if (ZoneConfig.empty())
55150fdfe39SJames Feist     {
55250fdfe39SJames Feist         std::cerr << "No fan zones, application pausing until reboot\n";
55350fdfe39SJames Feist         while (1)
55450fdfe39SJames Feist         {
55550fdfe39SJames Feist             bus.process_discard();
55650fdfe39SJames Feist         }
55750fdfe39SJames Feist     }
5587136a5aeSJames Feist }
5597136a5aeSJames Feist } // namespace dbus_configuration
560