blob: d9d9b43a57239be6d79430ce7e6a0406c42119b3 [file]
#ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_THERMAL_FAN_PID_CONTROLLER_H_
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_THERMAL_FAN_PID_CONTROLLER_H_
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/base/nullability.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include <nlohmann/json.hpp>
#include "thermal_config.pb.h"
#include "thermal_controller_specification.pb.h"
#include "tlbmc/thermal/controller/algorithm/pid.h"
#include "tlbmc/thermal/controller/controller.h"
#include "tlbmc/thermal/zone_manager.h"
namespace milotic_tlbmc::thermal {
constexpr double kDefaultFanPidSetpointValue = 0.0;
struct FanPidControllerParameters {
std::string id;
ZoneManager* absl_nonnull zone_manager ABSL_REQUIRE_EXPLICIT_INIT;
std::unique_ptr<PidThermalLoop> absl_nonnull pid_loop
ABSL_REQUIRE_EXPLICIT_INIT;
std::vector<std::string> input_fans;
std::vector<std::string> output_fans;
};
/**
* `FanPidController` integrates the outputs of other thermal controllers to
* process the final output for fans.
*
* Different from `PidController` taking sensor readings as input,
* `FanPidController` takes RPMs of fans and the maximum setpoints derived from
* other thermal controllers, and feeds them into a PID thermal loop algorithm
* to calculate the final PWM output for the fans.
*
* This class is not thread-safe.
*
* This class is created based on `fancontroller.*` files at
* https://source.corp.google.com/piper///depot/google3/third_party/openbmc_phosphor_pid_control/pid/
*/
class FanPidController : public ThermalController {
public:
struct FanPidControllerCoefficients {
std::optional<double> coeff_proportional;
std::optional<double> coeff_integral;
std::optional<double> coeff_derivative;
std::optional<double> feed_forward_offset;
std::optional<double> feed_forward_gain;
bool operator==(const FanPidControllerCoefficients& other) const = default;
};
~FanPidController() final = default;
static absl::StatusOr<std::unique_ptr<FanPidController>> Create(
FanPidControllerParameters params);
void ProcessThermalLoop() final;
double ProcessInput() final;
void ProcessOutput(double pwm) final;
std::string GetId() const final { return id_; }
bool IsFanController() const final { return true; }
bool IsSupportiveController() const final { return false; }
const std::vector<std::string>& GetAffectedControllerIds() const final {
return empty_affected_controller_ids_;
}
// The setpoint of a fan PID controller is provided by the zone manager
// directly.
void AdjustSetpoint([[maybe_unused]] double offset) final {}
double GetSetpoint() const { return zone_owner_->GetMaximumSetpointValue(); }
ZoneManager* GetZoneManager() const { return zone_owner_; }
PidThermalLoop* GetPidLoop() const { return pid_loop_.get(); }
void AddFan(const std::string& fan_name) { input_fans_.push_back(fan_name); }
absl::Span<const std::string> GetInputFans() const
ABSL_ATTRIBUTE_LIFETIME_BOUND {
return input_fans_;
}
absl::Span<const std::string> GetOutputFans() const
ABSL_ATTRIBUTE_LIFETIME_BOUND {
return output_fans_;
}
nlohmann::json ToJson() const;
// Setters of PID coefficients for tuning purposes.
void SetCoefficients(const FanPidControllerCoefficients& coefficients);
protected:
explicit FanPidController(
absl::string_view id, ZoneManager* absl_nonnull zone_manager,
std::unique_ptr<PidThermalLoop> absl_nonnull pid_loop,
std::vector<std::string> input_fans, std::vector<std::string> output_fans)
: ThermalController(),
id_(id),
zone_owner_(zone_manager),
pid_loop_(std::move(pid_loop)),
input_fans_(std::move(input_fans)),
output_fans_(std::move(output_fans)) {}
private:
const std::string id_;
// Use a raw pointer here to avoid circular dependency, as `ZoneManager`
// depends on `PidController` with `std::unique_ptr`.
// Every `ZoneManager` will be owned by the thermal control collector of
// tlBMC, and will live as long as the thermal control service is on.
ZoneManager* zone_owner_ = nullptr;
// Every `PidThermalLoop` will be owned by its thermal controller, and will
// live as long as the thermal control service is on.
std::unique_ptr<PidThermalLoop> pid_loop_;
std::vector<std::string> input_fans_;
std::vector<std::string> output_fans_;
// The previous failsafe state of this fan PID loop.
bool previous_failsafe_state_ = false;
// An empty vector to return by reference for `GetAffectedControllerIds`.
const std::vector<std::string> empty_affected_controller_ids_;
};
} // namespace milotic_tlbmc::thermal
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_THERMAL_FAN_PID_CONTROLLER_H_