diff options
| author | James Feist <james.feist@linux.intel.com> | 2018-08-31 14:07:12 -0700 |
|---|---|---|
| committer | Patrick Venture <venture@google.com> | 2018-09-12 14:38:03 +0000 |
| commit | 22c257abd27fd76ed5280ef5c717eef1f28bfca9 (patch) | |
| tree | fc7c19e32d37af0fddbe670c0f95c91820eca0cb | |
| parent | 8c3c51eed8584ad662014c10cc031b5bd8399415 (diff) | |
| download | phosphor-pid-control-22c257abd27fd76ed5280ef5c717eef1f28bfca9.tar.gz phosphor-pid-control-22c257abd27fd76ed5280ef5c717eef1f28bfca9.zip | |
Add stepwise controller
This adds the ability to use stepwise curves alongside
pid control. This creates a base controller class that
pidcontroller and stepwise controller inherit from.
Note: Hysteresis to come in follow-on patch
Tested-by:
Created a stepwise controller and noticed that when it
crossed a threshold that it contributed to the pwm setting.
Change-Id: I6cf842f80eaccafc905d620970afe91e2092d568
Signed-off-by: James Feist <james.feist@linux.intel.com>
| -rw-r--r-- | Makefile.am | 4 | ||||
| -rw-r--r-- | conf.hpp | 7 | ||||
| -rw-r--r-- | dbus/dbusconfiguration.cpp | 385 | ||||
| -rw-r--r-- | pid/builder.cpp | 18 | ||||
| -rw-r--r-- | pid/builderconfig.cpp | 30 | ||||
| -rw-r--r-- | pid/controller.hpp | 56 | ||||
| -rw-r--r-- | pid/ec/stepwise.cpp | 45 | ||||
| -rw-r--r-- | pid/ec/stepwise.hpp | 36 | ||||
| -rw-r--r-- | pid/fancontroller.hpp | 2 | ||||
| -rw-r--r-- | pid/pidcontroller.cpp (renamed from pid/controller.cpp) | 4 | ||||
| -rw-r--r-- | pid/pidcontroller.hpp | 60 | ||||
| -rw-r--r-- | pid/pidthread.cpp | 2 | ||||
| -rw-r--r-- | pid/stepwisecontroller.cpp | 75 | ||||
| -rw-r--r-- | pid/stepwisecontroller.hpp | 52 | ||||
| -rw-r--r-- | pid/thermalcontroller.hpp | 2 | ||||
| -rw-r--r-- | pid/zone.cpp | 9 | ||||
| -rw-r--r-- | pid/zone.hpp | 9 | ||||
| -rw-r--r-- | test/Makefile.am | 6 |
18 files changed, 590 insertions, 212 deletions
diff --git a/Makefile.am b/Makefile.am index a46ffd0..23f39bb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,9 +54,11 @@ libswampd_la_SOURCES = \ sensors/builderconfig.cpp \ sensors/manager.cpp \ pid/ec/pid.cpp \ - pid/controller.cpp \ + pid/ec/stepwise.cpp \ pid/fancontroller.cpp \ pid/thermalcontroller.cpp \ + pid/pidcontroller.cpp \ + pid/stepwisecontroller.cpp \ pid/builder.cpp \ pid/builderconfig.cpp \ pid/zone.cpp \ @@ -1,6 +1,7 @@ #pragma once #include "pid/ec/pid.hpp" +#include "pid/ec/stepwise.hpp" #include <map> #include <string> @@ -30,7 +31,11 @@ struct controller_info std::string type; // fan or margin or temp? std::vector<std::string> inputs; // one or more sensors. float setpoint; // initial setpoint for thermal. - ec::pidinfo info; // pid details + union + { + ec::pidinfo pidInfo; // pid details + ec::StepwiseInfo stepwiseInfo; + }; }; /* diff --git a/dbus/dbusconfiguration.cpp b/dbus/dbusconfiguration.cpp index 7f721bf..b912e62 100644 --- a/dbus/dbusconfiguration.cpp +++ b/dbus/dbusconfiguration.cpp @@ -21,6 +21,7 @@ #include <iostream> #include <sdbusplus/bus.hpp> #include <sdbusplus/bus/match.hpp> +#include <sdbusplus/exception.hpp> #include <set> #include <thread> #include <unordered_map> @@ -37,6 +38,8 @@ constexpr const char* objectManagerInterface = "org.freedesktop.DBus.ObjectManager"; constexpr const char* pidZoneConfigurationInterface = "xyz.openbmc_project.Configuration.Pid.Zone"; +constexpr const char* stepwiseConfigurationInterface = + "xyz.openbmc_project.Configuration.Stepwise"; constexpr const char* sensorInterface = "xyz.openbmc_project.Sensor.Value"; constexpr const char* pwmInterface = "xyz.openbmc_project.Control.FanPwm"; @@ -103,17 +106,17 @@ void debugPrint(void) } std::cout << "\t\t\t}\n"; std::cout << "\t\t\t" << pidconf.second.setpoint << ",\n"; - std::cout << "\t\t\t{" << pidconf.second.info.ts << ",\n"; - std::cout << "\t\t\t" << pidconf.second.info.p_c << ",\n"; - std::cout << "\t\t\t" << pidconf.second.info.i_c << ",\n"; - std::cout << "\t\t\t" << pidconf.second.info.ff_off << ",\n"; - std::cout << "\t\t\t" << pidconf.second.info.ff_gain << ",\n"; - std::cout << "\t\t\t{" << pidconf.second.info.i_lim.min << "," - << pidconf.second.info.i_lim.max << "},\n"; - std::cout << "\t\t\t{" << pidconf.second.info.out_lim.min << "," - << pidconf.second.info.out_lim.max << "},\n"; - std::cout << "\t\t\t" << pidconf.second.info.slew_neg << ",\n"; - std::cout << "\t\t\t" << pidconf.second.info.slew_pos << ",\n"; + std::cout << "\t\t\t{" << pidconf.second.pidInfo.ts << ",\n"; + std::cout << "\t\t\t" << pidconf.second.pidInfo.p_c << ",\n"; + std::cout << "\t\t\t" << pidconf.second.pidInfo.i_c << ",\n"; + std::cout << "\t\t\t" << pidconf.second.pidInfo.ff_off << ",\n"; + std::cout << "\t\t\t" << pidconf.second.pidInfo.ff_gain << ",\n"; + std::cout << "\t\t\t{" << pidconf.second.pidInfo.i_lim.min << "," + << pidconf.second.pidInfo.i_lim.max << "},\n"; + std::cout << "\t\t\t{" << pidconf.second.pidInfo.out_lim.min << "," + << pidconf.second.pidInfo.out_lim.max << "},\n"; + std::cout << "\t\t\t" << pidconf.second.pidInfo.slew_neg << ",\n"; + std::cout << "\t\t\t" << pidconf.second.pidInfo.slew_pos << ",\n"; std::cout << "\t\t\t}\n\t\t}\n"; } std::cout << "\t},\n"; @@ -123,14 +126,15 @@ void debugPrint(void) void init(sdbusplus::bus::bus& bus) { + using DbusVariantType = + sdbusplus::message::variant<uint64_t, int64_t, double, std::string, + std::vector<std::string>, + std::vector<double>>; + using ManagedObjectType = std::unordered_map< sdbusplus::message::object_path, - std::unordered_map< - std::string, - std::unordered_map<std::string, - sdbusplus::message::variant< - uint64_t, int64_t, double, std::string, - std::vector<std::string>>>>>; + std::unordered_map<std::string, + std::unordered_map<std::string, DbusVariantType>>>; // install watch for properties changed std::function<void(sdbusplus::message::message & message)> eventHandler = @@ -153,22 +157,32 @@ void init(sdbusplus::bus::bus& bus) "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", "GetSubTree"); mapper.append("", 0, - std::array<const char*, 5>{objectManagerInterface, + std::array<const char*, 6>{objectManagerInterface, pidConfigurationInterface, pidZoneConfigurationInterface, + stepwiseConfigurationInterface, sensorInterface, pwmInterface}); - auto resp = bus.call(mapper); - if (resp.is_method_error()) - { - throw std::runtime_error("ObjectMapper Call Failure"); - } std::unordered_map< std::string, std::unordered_map<std::string, std::vector<std::string>>> respData; + try + { + auto resp = bus.call(mapper); + if (resp.is_method_error()) + { + throw std::runtime_error("ObjectMapper Call Failure"); + } + resp.read(respData); + } + catch (sdbusplus::exception_t&) + { + // can't do anything without mapper call data + throw std::runtime_error("ObjectMapper Call Failure"); + } - resp.read(respData); if (respData.empty()) { + // can't do anything without mapper call data throw std::runtime_error("No configuration data available from Mapper"); } // create a map of pair of <has pid configuration, ObjectManager path> @@ -188,7 +202,8 @@ void init(sdbusplus::bus::bus& bus) owner.second = objectPair.first; } if (interface == pidConfigurationInterface || - interface == pidZoneConfigurationInterface) + interface == pidZoneConfigurationInterface || + interface == stepwiseConfigurationInterface) { owner.first = true; } @@ -216,19 +231,31 @@ void init(sdbusplus::bus::bus& bus) auto endpoint = bus.new_method_call( owner.first.c_str(), owner.second.second.c_str(), "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - auto responce = bus.call(endpoint); - if (responce.is_method_error()) + ManagedObjectType configuration; + try { + auto responce = bus.call(endpoint); + if (responce.is_method_error()) + { + throw std::runtime_error("Error getting managed objects from " + + owner.first); + } + responce.read(configuration); + } + catch (sdbusplus::exception_t&) + { + // this shouldn't happen, probably means daemon crashed throw std::runtime_error("Error getting managed objects from " + owner.first); } - ManagedObjectType configuration; - responce.read(configuration); + for (auto& pathPair : configuration) { if (pathPair.second.find(pidConfigurationInterface) != pathPair.second.end() || pathPair.second.find(pidZoneConfigurationInterface) != + pathPair.second.end() || + pathPair.second.find(stepwiseConfigurationInterface) != pathPair.second.end()) { configurations.emplace(pathPair); @@ -268,125 +295,217 @@ void init(sdbusplus::bus::bus& bus) VariantToFloatVisitor(), zone.at("FailSafePercent")); } auto findBase = configuration.second.find(pidConfigurationInterface); - if (findBase == configuration.second.end()) + if (findBase != configuration.second.end()) { - continue; - } - const auto& base = configuration.second.at(pidConfigurationInterface); - const std::vector<std::string>& zones = - sdbusplus::message::variant_ns::get<std::vector<std::string>>( - base.at("Zones")); - for (const std::string& zone : zones) - { - auto it = std::find(zoneIndex.begin(), zoneIndex.end(), zone); - size_t index = 1; - if (it == zoneIndex.end()) - { - zoneIndex.emplace_back(zone); - index = zoneIndex.size(); - } - else - { - index = zoneIndex.end() - it; - } - PIDConf& conf = ZoneConfig[index]; - struct controller_info& info = - conf[sdbusplus::message::variant_ns::get<std::string>( - base.at("Name"))]; - info.type = sdbusplus::message::variant_ns::get<std::string>( - base.at("Class")); - // todo: auto generation yaml -> c script seems to discard this - // value for fans, verify this is okay - if (info.type == "fan") - { - info.setpoint = 0; - } - else - { - info.setpoint = mapbox::util::apply_visitor( - VariantToFloatVisitor(), base.at("SetPoint")); - } - info.info.ts = 1.0; // currently unused - info.info.p_c = mapbox::util::apply_visitor( - VariantToFloatVisitor(), base.at("PCoefficient")); - info.info.i_c = mapbox::util::apply_visitor( - VariantToFloatVisitor(), base.at("ICoefficient")); - info.info.ff_off = mapbox::util::apply_visitor( - VariantToFloatVisitor(), base.at("FFOffCoefficient")); - info.info.ff_gain = mapbox::util::apply_visitor( - VariantToFloatVisitor(), base.at("FFGainCoefficient")); - info.info.i_lim.max = mapbox::util::apply_visitor( - VariantToFloatVisitor(), base.at("ILimitMax")); - info.info.i_lim.min = mapbox::util::apply_visitor( - VariantToFloatVisitor(), base.at("ILimitMin")); - info.info.out_lim.max = mapbox::util::apply_visitor( - VariantToFloatVisitor(), base.at("OutLimitMax")); - info.info.out_lim.min = mapbox::util::apply_visitor( - VariantToFloatVisitor(), base.at("OutLimitMin")); - info.info.slew_neg = mapbox::util::apply_visitor( - VariantToFloatVisitor(), base.at("SlewNeg")); - info.info.slew_pos = mapbox::util::apply_visitor( - VariantToFloatVisitor(), base.at("SlewPos")); - - std::vector<std::string> sensorNames = + const auto& base = + configuration.second.at(pidConfigurationInterface); + const std::vector<std::string>& zones = sdbusplus::message::variant_ns::get<std::vector<std::string>>( - base.at("Inputs")); - auto findOutputs = - base.find("Outputs"); // currently only fans have outputs - if (findOutputs != base.end()) + base.at("Zones")); + for (const std::string& zone : zones) { - std::vector<std::string> outputs = + auto it = std::find(zoneIndex.begin(), zoneIndex.end(), zone); + size_t index = 1; + if (it == zoneIndex.end()) + { + zoneIndex.emplace_back(zone); + index = zoneIndex.size(); + } + else + { + index = zoneIndex.end() - it; + } + PIDConf& conf = ZoneConfig[index]; + struct controller_info& info = + conf[sdbusplus::message::variant_ns::get<std::string>( + base.at("Name"))]; + info.type = sdbusplus::message::variant_ns::get<std::string>( + base.at("Class")); + // todo: auto generation yaml -> c script seems to discard this + // value for fans, verify this is okay + if (info.type == "fan") + { + info.setpoint = 0; + } + else + { + info.setpoint = mapbox::util::apply_visitor( + VariantToFloatVisitor(), base.at("SetPoint")); + } + info.pidInfo.ts = 1.0; // currently unused + info.pidInfo.p_c = mapbox::util::apply_visitor( + VariantToFloatVisitor(), base.at("PCoefficient")); + info.pidInfo.i_c = mapbox::util::apply_visitor( + VariantToFloatVisitor(), base.at("ICoefficient")); + info.pidInfo.ff_off = mapbox::util::apply_visitor( + VariantToFloatVisitor(), base.at("FFOffCoefficient")); + info.pidInfo.ff_gain = mapbox::util::apply_visitor( + VariantToFloatVisitor(), base.at("FFGainCoefficient")); + info.pidInfo.i_lim.max = mapbox::util::apply_visitor( + VariantToFloatVisitor(), base.at("ILimitMax")); + info.pidInfo.i_lim.min = mapbox::util::apply_visitor( + VariantToFloatVisitor(), base.at("ILimitMin")); + info.pidInfo.out_lim.max = mapbox::util::apply_visitor( + VariantToFloatVisitor(), base.at("OutLimitMax")); + info.pidInfo.out_lim.min = mapbox::util::apply_visitor( + VariantToFloatVisitor(), base.at("OutLimitMin")); + info.pidInfo.slew_neg = mapbox::util::apply_visitor( + VariantToFloatVisitor(), base.at("SlewNeg")); + info.pidInfo.slew_pos = mapbox::util::apply_visitor( + VariantToFloatVisitor(), base.at("SlewPos")); + + std::vector<std::string> sensorNames = sdbusplus::message::variant_ns::get< - std::vector<std::string>>(findOutputs->second); - sensorNames.insert(sensorNames.end(), outputs.begin(), - outputs.end()); + std::vector<std::string>>(base.at("Inputs")); + auto findOutputs = + base.find("Outputs"); // currently only fans have outputs + if (findOutputs != base.end()) + { + std::vector<std::string> outputs = + sdbusplus::message::variant_ns::get< + std::vector<std::string>>(findOutputs->second); + sensorNames.insert(sensorNames.end(), outputs.begin(), + outputs.end()); + } + for (const std::string& sensorName : sensorNames) + { + std::string name = sensorName; + // replace spaces with underscores to be legal on dbus + std::replace(name.begin(), name.end(), ' ', '_'); + std::pair<std::string, std::string> sensorPathIfacePair; + + if (!findSensor(sensors, name, sensorPathIfacePair)) + { + throw std::runtime_error( + "Could not map configuration to sensor " + name); + } + if (sensorPathIfacePair.second == sensorInterface) + { + info.inputs.push_back(name); + auto& config = SensorConfig[name]; + config.type = + sdbusplus::message::variant_ns::get<std::string>( + base.at("Class")); + config.readpath = sensorPathIfacePair.first; + // todo: maybe un-hardcode this if we run into slower + // timeouts with sensors + if (config.type == "temp") + { + config.timeout = 500; + } + } + if (sensorPathIfacePair.second == pwmInterface) + { + // copy so we can modify it + for (std::string otherSensor : sensorNames) + { + if (otherSensor == sensorName) + { + continue; + } + std::replace(otherSensor.begin(), otherSensor.end(), + ' ', '_'); + auto& config = SensorConfig[otherSensor]; + config.writepath = sensorPathIfacePair.first; + // todo: un-hardcode this if there are fans with + // different ranges + config.max = 255; + config.min = 0; + } + } + } } - for (const std::string& sensorName : sensorNames) + } + auto findStepwise = + configuration.second.find(stepwiseConfigurationInterface); + if (findStepwise != configuration.second.end()) + { + const auto& base = findStepwise->second; + const std::vector<std::string>& zones = + sdbusplus::message::variant_ns::get<std::vector<std::string>>( + base.at("Zones")); + for (const std::string& zone : zones) { - std::string name = sensorName; - // replace spaces with underscores to be legal on dbus - std::replace(name.begin(), name.end(), ' ', '_'); - std::pair<std::string, std::string> sensorPathIfacePair; - - if (!findSensor(sensors, name, sensorPathIfacePair)) + auto it = std::find(zoneIndex.begin(), zoneIndex.end(), zone); + size_t index = 1; + if (it == zoneIndex.end()) + { + zoneIndex.emplace_back(zone); + index = zoneIndex.size(); + } + else + { + index = zoneIndex.end() - it; + } + PIDConf& conf = ZoneConfig[index]; + struct controller_info& info = + conf[sdbusplus::message::variant_ns::get<std::string>( + base.at("Name"))]; + info.type = "stepwise"; + info.stepwiseInfo.ts = 1.0; // currently unused + std::vector<double> readings = + sdbusplus::message::variant_ns::get<std::vector<double>>( + base.at("Reading")); + if (readings.size() > ec::maxStepwisePoints) + { + throw std::invalid_argument("Too many stepwise points."); + } + if (readings.empty()) + { + throw std::invalid_argument( + "Must have one stepwise point."); + } + std::copy(readings.begin(), readings.end(), + info.stepwiseInfo.reading); + if (readings.size() < ec::maxStepwisePoints) + { + info.stepwiseInfo.reading[readings.size()] = + std::numeric_limits<float>::quiet_NaN(); + } + std::vector<double> outputs = + sdbusplus::message::variant_ns::get<std::vector<double>>( + base.at("Output")); + if (readings.size() != outputs.size()) + { + throw std::invalid_argument( + "Outputs size must match readings"); + } + std::copy(outputs.begin(), outputs.end(), + info.stepwiseInfo.output); + if (outputs.size() < ec::maxStepwisePoints) { - throw std::runtime_error( - "Could not map configuration to sensor " + name); + info.stepwiseInfo.output[outputs.size()] = + std::numeric_limits<float>::quiet_NaN(); } - if (sensorPathIfacePair.second == sensorInterface) + + std::vector<std::string> sensorNames = + sdbusplus::message::variant_ns::get< + std::vector<std::string>>(base.at("Inputs")); + + for (const std::string& sensorName : sensorNames) { + std::string name = sensorName; + // replace spaces with underscores to be legal on dbus + std::replace(name.begin(), name.end(), ' ', '_'); + std::pair<std::string, std::string> sensorPathIfacePair; + + if (!findSensor(sensors, name, sensorPathIfacePair)) + { + // todo(james): if we can't find a sensor, delete it + // from config, we'll find it on rescan + throw std::runtime_error( + "Could not map configuration to sensor " + name); + } + info.inputs.push_back(name); auto& config = SensorConfig[name]; - config.type = - sdbusplus::message::variant_ns::get<std::string>( - base.at("Class")); config.readpath = sensorPathIfacePair.first; + config.type = "temp"; // todo: maybe un-hardcode this if we run into slower // timeouts with sensors - if (config.type == "temp") - { - config.timeout = 500; - } - } - if (sensorPathIfacePair.second == pwmInterface) - { - // copy so we can modify it - for (std::string otherSensor : sensorNames) - { - if (otherSensor == sensorName) - { - continue; - } - std::replace(otherSensor.begin(), otherSensor.end(), - ' ', '_'); - auto& config = SensorConfig[otherSensor]; - config.writepath = sensorPathIfacePair.first; - // todo: un-hardcode this if there are fans with - // different ranges - config.max = 255; - config.min = 0; - } + + config.timeout = 500; } } } diff --git a/pid/builder.cpp b/pid/builder.cpp index 8ffa77c..e98ba22 100644 --- a/pid/builder.cpp +++ b/pid/builder.cpp @@ -17,7 +17,9 @@ #include "pid/builder.hpp" #include "conf.hpp" +#include "pid/controller.hpp" #include "pid/fancontroller.hpp" +#include "pid/stepwisecontroller.hpp" #include "pid/thermalcontroller.hpp" #include <iostream> @@ -89,7 +91,7 @@ std::unordered_map<int64_t, std::unique_ptr<PIDZone>> } auto pid = FanController::CreateFanPid(zone.get(), name, inputs, - info->info); + info->pidInfo); zone->addFanPID(std::move(pid)); } else if (info->type == "temp" || info->type == "margin") @@ -101,9 +103,21 @@ std::unordered_map<int64_t, std::unique_ptr<PIDZone>> } auto pid = ThermalController::CreateThermalPid( - zone.get(), name, inputs, info->setpoint, info->info); + zone.get(), name, inputs, info->setpoint, info->pidInfo); + zone->addThermalPID(std::move(pid)); } + else if (info->type == "stepwise") + { + for (auto& i : info->inputs) + { + inputs.push_back(i); + zone->addThermalInput(i); + } + auto stepwise = StepwiseController::CreateStepwiseController( + zone.get(), name, inputs, info->stepwiseInfo); + zone->addThermalPID(std::move(stepwise)); + } std::cerr << "inputs: "; for (auto& i : inputs) diff --git a/pid/builderconfig.cpp b/pid/builderconfig.cpp index 0106057..c72ef9e 100644 --- a/pid/builderconfig.cpp +++ b/pid/builderconfig.cpp @@ -102,20 +102,22 @@ std::unordered_map<int64_t, std::unique_ptr<PIDZone>> /* set-point is only required to be set for thermal. */ /* TODO(venture): Verify this works optionally here. */ info.setpoint = pid.lookup("set-point"); - info.info.ts = pid.lookup("pid.sampleperiod"); - info.info.p_c = pid.lookup("pid.p_coefficient"); - info.info.i_c = pid.lookup("pid.i_coefficient"); - info.info.ff_off = pid.lookup("pid.ff_off_coefficient"); - info.info.ff_gain = pid.lookup("pid.ff_gain_coefficient"); - info.info.i_lim.min = pid.lookup("pid.i_limit.min"); - info.info.i_lim.max = pid.lookup("pid.i_limit.max"); - info.info.out_lim.min = pid.lookup("pid.out_limit.min"); - info.info.out_lim.max = pid.lookup("pid.out_limit.max"); - info.info.slew_neg = pid.lookup("pid.slew_neg"); - info.info.slew_pos = pid.lookup("pid.slew_pos"); - - std::cerr << "out_lim.min: " << info.info.out_lim.min << "\n"; - std::cerr << "out_lim.max: " << info.info.out_lim.max << "\n"; + info.pidInfo.ts = pid.lookup("pid.sampleperiod"); + info.pidInfo.p_c = pid.lookup("pid.p_coefficient"); + info.pidInfo.i_c = pid.lookup("pid.i_coefficient"); + info.pidInfo.ff_off = pid.lookup("pid.ff_off_coefficient"); + info.pidInfo.ff_gain = pid.lookup("pid.ff_gain_coefficient"); + info.pidInfo.i_lim.min = pid.lookup("pid.i_limit.min"); + info.pidInfo.i_lim.max = pid.lookup("pid.i_limit.max"); + info.pidInfo.out_lim.min = pid.lookup("pid.out_limit.min"); + info.pidInfo.out_lim.max = pid.lookup("pid.out_limit.max"); + info.pidInfo.slew_neg = pid.lookup("pid.slew_neg"); + info.pidInfo.slew_pos = pid.lookup("pid.slew_pos"); + + std::cerr << "out_lim.min: " << info.pidInfo.out_lim.min + << "\n"; + std::cerr << "out_lim.max: " << info.pidInfo.out_lim.max + << "\n"; const Setting& inputs = pid["inputs"]; int icount = inputs.getLength(); diff --git a/pid/controller.hpp b/pid/controller.hpp index 57ee43f..f52c412 100644 --- a/pid/controller.hpp +++ b/pid/controller.hpp @@ -3,57 +3,23 @@ #include "ec/pid.hpp" #include "fan.hpp" -#include <memory> -#include <vector> - -class ZoneInterface; +#include <string> /* - * Base class for PID controllers. Each PID that implements this needs to - * provide an input_proc, setpt_proc, and output_proc. + * Base class for controllers. Each controller that implements this needs to + * provide an input_proc, process, and output_proc. */ -class PIDController -{ - public: - PIDController(const std::string& id, ZoneInterface* owner) : - _owner(owner), _setpoint(0), _id(id) - { - } +class ZoneInterface; - virtual ~PIDController() - { - } +struct Controller +{ + virtual ~Controller() = default; virtual float input_proc(void) = 0; - virtual float setpt_proc(void) = 0; + virtual void output_proc(float value) = 0; - void pid_process(void); - - std::string get_id(void) - { - return _id; - } - float get_setpoint(void) - { - return _setpoint; - } - void set_setpoint(float setpoint) - { - _setpoint = setpoint; - } - - ec::pid_info_t* get_pid_info(void) - { - return &_pid_info; - } - - protected: - ZoneInterface* _owner; - - private: - // parameters - ec::pid_info_t _pid_info; - float _setpoint; - std::string _id; + virtual void process(void) = 0; + + virtual std::string get_id(void) = 0; }; diff --git a/pid/ec/stepwise.cpp b/pid/ec/stepwise.cpp new file mode 100644 index 0000000..4246fb3 --- /dev/null +++ b/pid/ec/stepwise.cpp @@ -0,0 +1,45 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "stepwise.hpp" + +#include <cstddef> +#include <limits> + +namespace ec +{ +float stepwise(const ec::StepwiseInfo& info, float input) +{ + float value = info.output[0]; // if we are below the lowest + // point, we set the lowest value + + for (size_t ii = 1; ii < ec::maxStepwisePoints; ii++) + { + + if (info.reading[ii] == std::numeric_limits<float>::quiet_NaN()) + { + break; + } + if (info.reading[ii] > input) + { + break; + } + value = info.output[ii]; + } + + return value; +} +} // namespace ec
\ No newline at end of file diff --git a/pid/ec/stepwise.hpp b/pid/ec/stepwise.hpp new file mode 100644 index 0000000..ed07b44 --- /dev/null +++ b/pid/ec/stepwise.hpp @@ -0,0 +1,36 @@ + +/* +// Copyright (c) 2018 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#pragma once + +#include <cstddef> +#include <vector> + +namespace ec +{ +constexpr size_t maxStepwisePoints = 20; + +struct StepwiseInfo +{ + float ts; // sample time in seconds + float reading[maxStepwisePoints]; + float output[maxStepwisePoints]; +}; + +float stepwise(const ec::StepwiseInfo& info, float value); + +} // namespace ec
\ No newline at end of file diff --git a/pid/fancontroller.hpp b/pid/fancontroller.hpp index aaccd4b..521d638 100644 --- a/pid/fancontroller.hpp +++ b/pid/fancontroller.hpp @@ -1,8 +1,8 @@ #pragma once -#include "controller.hpp" #include "ec/pid.hpp" #include "fan.hpp" +#include "pidcontroller.hpp" #include <memory> #include <string> diff --git a/pid/controller.cpp b/pid/pidcontroller.cpp index 138268b..524b0ef 100644 --- a/pid/controller.cpp +++ b/pid/pidcontroller.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "controller.hpp" +#include "pidcontroller.hpp" #include "ec/pid.hpp" @@ -26,7 +26,7 @@ #include <thread> #include <vector> -void PIDController::pid_process(void) +void PIDController::process(void) { float input; float setpt; diff --git a/pid/pidcontroller.hpp b/pid/pidcontroller.hpp new file mode 100644 index 0000000..18a448e --- /dev/null +++ b/pid/pidcontroller.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "controller.hpp" +#include "ec/pid.hpp" +#include "fan.hpp" + +#include <memory> +#include <vector> + +class ZoneInterface; + +/* + * Base class for PID controllers. Each PID that implements this needs to + * provide an input_proc, setpt_proc, and output_proc. + */ +class PIDController : public Controller +{ + public: + PIDController(const std::string& id, ZoneInterface* owner) : + Controller(), _owner(owner), _setpoint(0), _id(id) + { + } + + virtual ~PIDController() + { + } + + virtual float input_proc(void) = 0; + virtual float setpt_proc(void) = 0; + virtual void output_proc(float value) = 0; + + void process(void); + + std::string get_id(void) + { + return _id; + } + float get_setpoint(void) + { + return _setpoint; + } + void set_setpoint(float setpoint) + { + _setpoint = setpoint; + } + + ec::pid_info_t* get_pid_info(void) + { + return &_pid_info; + } + + protected: + ZoneInterface* _owner; + + private: + // parameters + ec::pid_info_t _pid_info; + float _setpoint; + std::string _id; +}; diff --git a/pid/pidthread.cpp b/pid/pidthread.cpp index 490b70e..4dfc22a 100644 --- a/pid/pidthread.cpp +++ b/pid/pidthread.cpp @@ -16,7 +16,7 @@ #include "pidthread.hpp" -#include "pid/controller.hpp" +#include "pid/pidcontroller.hpp" #include "sensors/sensor.hpp" #include <chrono> diff --git a/pid/stepwisecontroller.cpp b/pid/stepwisecontroller.cpp new file mode 100644 index 0000000..cea21e5 --- /dev/null +++ b/pid/stepwisecontroller.cpp @@ -0,0 +1,75 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "stepwisecontroller.hpp" + +#include "ec/stepwise.hpp" +#include "util.hpp" +#include "zone.hpp" + +#include <algorithm> +#include <chrono> +#include <iostream> +#include <map> +#include <memory> +#include <thread> +#include <vector> + +void StepwiseController::process(void) +{ + // Get input value + float input = input_proc(); + + // Calculate new output + float output = ec::stepwise(get_stepwise_info(), input); + + // Output new value + output_proc(output); + + return; +} + +std::unique_ptr<Controller> StepwiseController::CreateStepwiseController( + ZoneInterface* owner, const std::string& id, + const std::vector<std::string>& inputs, const ec::StepwiseInfo& initial) +{ + // StepwiseController currently only supports precisely one input. + if (inputs.size() != 1) + { + return nullptr; + } + + auto thermal = std::make_unique<StepwiseController>(id, inputs, owner); + + ec::StepwiseInfo& info = thermal->get_stepwise_info(); + + info = initial; + + return thermal; +} + +float StepwiseController::input_proc(void) +{ + double value = _owner->getCachedValue(_inputs.at(0)); + return static_cast<float>(value); +} + +void StepwiseController::output_proc(float value) +{ + _owner->addRPMSetPoint(value); + + return; +} diff --git a/pid/stepwisecontroller.hpp b/pid/stepwisecontroller.hpp new file mode 100644 index 0000000..c3af1a1 --- /dev/null +++ b/pid/stepwisecontroller.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "controller.hpp" +#include "ec/stepwise.hpp" +#include "fan.hpp" + +#include <memory> +#include <vector> + +class ZoneInterface; + +class StepwiseController : public Controller +{ + public: + static std::unique_ptr<Controller> + CreateStepwiseController(ZoneInterface* owner, const std::string& id, + const std::vector<std::string>& inputs, + const ec::StepwiseInfo& initial); + + StepwiseController(const std::string& id, + const std::vector<std::string>& inputs, + ZoneInterface* owner) : + Controller(), + _owner(owner), _id(id), _inputs(inputs) + { + } + + float input_proc(void) override; + + void output_proc(float value) override; + + void process(void) override; + + std::string get_id(void) + { + return _id; + } + + ec::StepwiseInfo& get_stepwise_info(void) + { + return _stepwise_info; + } + + protected: + ZoneInterface* _owner; + + private: + // parameters + ec::StepwiseInfo _stepwise_info; + std::string _id; + std::vector<std::string> _inputs; +}; diff --git a/pid/thermalcontroller.hpp b/pid/thermalcontroller.hpp index ed94acb..85c3a2b 100644 --- a/pid/thermalcontroller.hpp +++ b/pid/thermalcontroller.hpp @@ -1,7 +1,7 @@ #pragma once -#include "controller.hpp" #include "ec/pid.hpp" +#include "pidcontroller.hpp" #include <memory> #include <string> diff --git a/pid/zone.cpp b/pid/zone.cpp index 9e948f0..92a332e 100644 --- a/pid/zone.cpp +++ b/pid/zone.cpp @@ -21,6 +21,7 @@ #include "pid/controller.hpp" #include "pid/ec/pid.hpp" #include "pid/fancontroller.hpp" +#include "pid/stepwisecontroller.hpp" #include "pid/thermalcontroller.hpp" #include <algorithm> @@ -80,12 +81,12 @@ float PIDZone::getMinThermalRpmSetPt(void) const return _minThermalRpmSetPt; } -void PIDZone::addFanPID(std::unique_ptr<PIDController> pid) +void PIDZone::addFanPID(std::unique_ptr<Controller> pid) { _fans.push_back(std::move(pid)); } -void PIDZone::addThermalPID(std::unique_ptr<PIDController> pid) +void PIDZone::addThermalPID(std::unique_ptr<Controller> pid) { _thermals.push_back(std::move(pid)); } @@ -305,7 +306,7 @@ void PIDZone::process_fans(void) { for (auto& p : _fans) { - p->pid_process(); + p->process(); } } @@ -313,7 +314,7 @@ void PIDZone::process_thermals(void) { for (auto& p : _thermals) { - p->pid_process(); + p->process(); } } diff --git a/pid/zone.hpp b/pid/zone.hpp index 99a8f14..6d5f6cc 100644 --- a/pid/zone.hpp +++ b/pid/zone.hpp @@ -2,6 +2,7 @@ #include "conf.hpp" #include "controller.hpp" +#include "pidcontroller.hpp" #include "sensors/manager.hpp" #include "sensors/sensor.hpp" #include "xyz/openbmc_project/Control/Mode/server.hpp" @@ -76,8 +77,8 @@ class PIDZone : public ZoneInterface, public ModeObject void process_fans(void); void process_thermals(void); - void addFanPID(std::unique_ptr<PIDController> pid); - void addThermalPID(std::unique_ptr<PIDController> pid); + void addFanPID(std::unique_ptr<Controller> pid); + void addThermalPID(std::unique_ptr<Controller> pid); double getCachedValue(const std::string& name) override; void addFanInput(std::string fan); void addThermalInput(std::string therm); @@ -111,6 +112,6 @@ class PIDZone : public ZoneInterface, public ModeObject std::map<std::string, double> _cachedValuesByName; const SensorManager& _mgr; - std::vector<std::unique_ptr<PIDController>> _fans; - std::vector<std::unique_ptr<PIDController>> _thermals; + std::vector<std::unique_ptr<Controller>> _fans; + std::vector<std::unique_ptr<Controller>> _thermals; }; diff --git a/test/Makefile.am b/test/Makefile.am index 6622169..4a01994 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -32,17 +32,17 @@ util_unittest_LDADD = $(top_builddir)/util.o pid_zone_unittest_SOURCES = pid_zone_unittest.cpp pid_zone_unittest_LDADD = $(top_builddir)/pid/ec/pid.o \ - $(top_builddir)/sensors/manager.o $(top_builddir)/pid/controller.o \ + $(top_builddir)/sensors/manager.o $(top_builddir)/pid/pidcontroller.o \ $(top_builddir)/pid/zone.o pid_thermalcontroller_unittest_SOURCES = pid_thermalcontroller_unittest.cpp pid_thermalcontroller_unittest_LDADD = $(top_builddir)/pid/ec/pid.o \ - $(top_builddir)/pid/util.o $(top_builddir)/pid/controller.o \ + $(top_builddir)/pid/util.o $(top_builddir)/pid/pidcontroller.o \ $(top_builddir)/pid/thermalcontroller.o pid_fancontroller_unittest_SOURCES = pid_fancontroller_unittest.cpp pid_fancontroller_unittest_LDADD = $(top_builddir)/pid/ec/pid.o \ - $(top_builddir)/pid/util.o $(top_builddir)/pid/controller.o \ + $(top_builddir)/pid/util.o $(top_builddir)/pid/pidcontroller.o \ $(top_builddir)/pid/fancontroller.o dbus_passive_unittest_SOURCES = dbus_passive_unittest.cpp |

