| #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_ |