| #ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_THERMAL_EAT_CONTROLLER_H_ |
| #define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_THERMAL_EAT_CONTROLLER_H_ |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/base/attributes.h" |
| #include "absl/base/nullability.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_format.h" |
| #include "absl/strings/string_view.h" |
| #include <nlohmann/json_fwd.hpp> |
| #include "thermal_config.pb.h" |
| #include "thermal_controller_specification.pb.h" |
| #include "tlbmc/thermal/controller/algorithm/eat.h" |
| #include "tlbmc/thermal/controller/controller.h" |
| #include "tlbmc/thermal/zone_manager.h" |
| |
| namespace milotic_tlbmc::thermal { |
| |
| struct EatControllerParameters { |
| std::string id; |
| ZoneManager* absl_nonnull zone_manager ABSL_REQUIRE_EXPLICIT_INIT; |
| std::unique_ptr<EatThermalLoop> absl_nonnull eat_loop |
| ABSL_REQUIRE_EXPLICIT_INIT; |
| // Normally, EAT controller will take in readings from a single sensor. |
| // However, it is totally reasonable for it to utilize multiple sensors and |
| // take the average reading as the input. |
| std::vector<std::string> input_sensors; |
| std::vector<ZoneManager*> affected_zones; |
| std::vector<int> affected_zone_ids; |
| }; |
| |
| /** |
| * `EatController` reads from ambient temperature sensors and adjusts the |
| * setpoints of the affected thermal controllers. |
| * |
| * This is a special supportive controller, which does not directly control any |
| * actuators, or directly contribute to the thermal output. Instead, it adjusts |
| * the input setpoint of the other thermal controllers. |
| * |
| * Due to its slow process rate (e.g., once per day), it is, usually, better to |
| * create a separated thermal zone for this controller, with a large thermal |
| * cycle interval. |
| * |
| * This class is not thread-safe. |
| */ |
| class EatController : public ThermalController { |
| public: |
| ~EatController() final = default; |
| |
| static absl::StatusOr<std::unique_ptr<EatController>> Create( |
| EatControllerParameters params); |
| |
| void ProcessThermalLoop() final; |
| double ProcessInput() final; |
| void ProcessOutput(double output) final; |
| |
| ZoneManager* GetZoneManager() const { return zone_owner_; } |
| |
| EatThermalLoop* GetEatLoop() const { return eat_loop_.get(); } |
| |
| std::string GetId() const final { return id_; } |
| bool IsFanController() const final { return false; } |
| bool IsSupportiveController() const final { return true; } |
| // EAT controller has no setpoint to be adjusted. |
| void AdjustSetpoint([[maybe_unused]] double offset) final {} |
| |
| absl::Status SetAffectedZones(std::vector<ZoneManager*> affected_zones) { |
| if (affected_zones.empty()) { |
| return absl::InvalidArgumentError(absl::StrFormat( |
| "No affected zone is assigned to EAT controller `%s`!", id_)); |
| } |
| |
| affected_zones_ = std::move(affected_zones); |
| affected_zone_ids_.clear(); |
| for (const ZoneManager* zone : affected_zones_) { |
| affected_zone_ids_.push_back(zone->GetId()); |
| } |
| return absl::OkStatus(); |
| } |
| const std::vector<int>& GetAffectedZoneIds() const { |
| return affected_zone_ids_; |
| } |
| |
| const std::vector<std::string>& GetAffectedControllerIds() const final { |
| return empty_affected_controller_ids_; |
| } |
| |
| nlohmann::json ToJson() const; |
| |
| protected: |
| explicit EatController(absl::string_view id, ZoneManager* zone_manager, |
| std::unique_ptr<EatThermalLoop> absl_nonnull eat_loop, |
| std::vector<std::string> input_sensors, |
| std::vector<ZoneManager*> affected_zones, |
| std::vector<int> affected_zone_ids) |
| : ThermalController(), |
| id_(id), |
| zone_owner_(zone_manager), |
| eat_loop_(std::move(eat_loop)), |
| input_sensors_(std::move(input_sensors)), |
| affected_zones_(std::move(affected_zones)), |
| affected_zone_ids_(std::move(affected_zone_ids)) {} |
| |
| private: |
| const std::string id_; |
| |
| // Use a raw pointer here to avoid circular dependency, as `ZoneManager` |
| // depends on `EatController` 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 `EatThermalLoop` will be owned by its thermal controller, and will |
| // live as long as the thermal control service is on. |
| std::unique_ptr<EatThermalLoop> eat_loop_; |
| std::vector<std::string> input_sensors_; |
| std::vector<ZoneManager*> affected_zones_; |
| std::vector<int> affected_zone_ids_; |
| |
| // 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_EAT_CONTROLLER_H_ |