blob: ddb5893942faf4f44d7ece6784a9e37fda112410 [file] [log] [blame]
/**
* Copyright 2017 Google Inc.
*
* 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 "thermalcontroller.hpp"
#include "errors/exception.hpp"
#include "tuning.hpp"
#include "util.hpp"
#include "zone.hpp"
#include <algorithm>
#include <cmath>
#include <iostream>
namespace pid_control
{
ThermalType getThermalType(const std::string& typeString)
{
if (typeString == "margin")
{
return ThermalType::margin;
}
if ((typeString == "temp") || (typeString == "power"))
{
return ThermalType::absolute;
}
if (typeString == "powersum")
{
return ThermalType::summation;
}
throw ControllerBuildException("Unrecognized PID Type/Class string");
}
bool isThermalType(const std::string& typeString)
{
static const std::vector<std::string> thermalTypes = {"temp", "margin",
"power", "powersum"};
return std::count(thermalTypes.begin(), thermalTypes.end(), typeString);
}
std::unique_ptr<PIDController> ThermalController::createThermalPid(
ZoneInterface* owner, const std::string& id,
const std::vector<pid_control::conf::SensorInput>& inputs, double setpoint,
const ec::pidinfo& initial, const ThermalType& type)
{
// ThermalController requires at least 1 input
if (inputs.empty())
{
throw ControllerBuildException("Thermal controller missing inputs");
}
auto thermal = std::make_unique<ThermalController>(id, inputs, type, owner);
ec::pid_info_t* info = thermal->getPIDInfo();
thermal->setSetpoint(setpoint);
initializePIDStruct(info, initial);
return thermal;
}
// bmc_host_sensor_value_double
double ThermalController::inputProc(void)
{
double value;
const double& (*compare)(const double&, const double&);
bool doSummation = false;
if (type == ThermalType::margin)
{
value = std::numeric_limits<double>::max();
compare = std::min<double>;
}
else if (type == ThermalType::absolute)
{
value = std::numeric_limits<double>::lowest();
compare = std::max<double>;
}
else if (type == ThermalType::summation)
{
doSummation = true;
value = 0.0;
}
else
{
throw ControllerBuildException("Unrecognized ThermalType");
}
std::string leaderName = _inputs.begin()->name;
bool acceptable = false;
for (const auto& in : _inputs)
{
double cachedValue = _owner->getCachedValue(in.name);
// Less than 0 is perfectly OK for temperature, but must not be NAN
if (!(std::isfinite(cachedValue)))
{
continue;
}
// Perform TempToMargin conversion before further processing
if (type == ThermalType::margin)
{
if (in.convertTempToMargin)
{
if (!(std::isfinite(in.convertMarginZero)))
{
throw ControllerBuildException("Unrecognized TempToMargin");
}
double marginValue = in.convertMarginZero - cachedValue;
if (debugEnabled)
{
std::cerr << "Converting temp to margin: temp "
<< cachedValue << ", Tjmax "
<< in.convertMarginZero << ", margin "
<< marginValue << "\n";
}
cachedValue = marginValue;
}
}
double oldValue = value;
if (doSummation)
{
value += cachedValue;
}
else
{
value = compare(value, cachedValue);
}
if (oldValue != value)
{
leaderName = in.name;
_owner->updateThermalPowerDebugInterface(_id, leaderName, value, 0);
}
acceptable = true;
}
if (!acceptable)
{
// If none of the inputs were acceptable, use the setpoint as
// the input value. This will continue to run the PID loop, but
// make it a no-op, as the error will be zero. This provides safe
// behavior until the inputs become acceptable.
value = setptProc();
}
if (debugEnabled)
{
std::cerr << getID() << " choose the temperature value: " << value
<< " " << leaderName << "\n";
}
return value;
}
// bmc_get_setpt
double ThermalController::setptProc(void)
{
double setpoint = getSetpoint();
/* TODO(venture): Thermal setpoint invalid? */
#if 0
if (-1 == setpoint)
{
return 0.0f;
}
else
{
return setpoint;
}
#endif
return setpoint;
}
// bmc_set_pid_output
void ThermalController::outputProc(double value)
{
_owner->addSetPoint(value, _id);
_owner->updateThermalPowerDebugInterface(_id, "", 0, value);
if (debugEnabled)
{
std::cerr << getID() << " pid output pwm: " << value << "\n";
}
return;
}
} // namespace pid_control