| #include "tlbmc/thermal/controller/dff_controller.h" |
| |
| #include <algorithm> |
| #include <cmath> |
| #include <limits> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/memory/memory.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_format.h" |
| #include <nlohmann/json_fwd.hpp> |
| |
| namespace milotic_tlbmc { |
| namespace thermal { |
| |
| absl::StatusOr<std::unique_ptr<DffController>> DffController::Create( |
| DffControllerParameters params) { |
| if (params.input_sensors.empty()) { |
| return absl::InvalidArgumentError(absl::StrFormat( |
| "No input sensor is assigned to DFF controller `%s`!", params.id)); |
| } |
| if (params.affected_controller_ids.empty()) { |
| return absl::InvalidArgumentError(absl::StrFormat( |
| "No affected controller is assigned to DFF controller `%s`!", |
| params.id)); |
| } |
| |
| auto dff_controller = absl::WrapUnique(new DffController( |
| params.id, params.zone_manager, std::move(params.dff_loop), |
| std::move(params.input_sensors), |
| std::move(params.affected_controller_ids))); |
| |
| return dff_controller; |
| } |
| |
| void DffController::ProcessThermalLoop() { |
| const double input = ProcessInput(); |
| |
| // If the input is invalid, use the previous output. |
| double output = GetLastOutput(); |
| if (std::isfinite(input)) { |
| output = |
| GetDffLoop()->ExecuteDffLoop(input, zone_owner_->GetDebugDffEnabled()); |
| } |
| |
| ProcessOutput(output); |
| } |
| |
| double DffController::ProcessInput() { |
| double input_value = std::numeric_limits<double>::lowest(); |
| bool is_input_acceptable = false; |
| |
| for (const std::string& sensor : input_sensors_) { |
| double sensor_value = zone_owner_->GetSensorReadings(sensor).scaled_value; |
| |
| // Sensor readings must not be NAN. |
| if (!std::isfinite(sensor_value)) { |
| continue; |
| } |
| |
| input_value = std::max(input_value, sensor_value); |
| is_input_acceptable = true; |
| } |
| |
| // If no input is acceptable, return NaN to indicate the invalidness. |
| if (!is_input_acceptable) { |
| input_value = std::numeric_limits<double>::quiet_NaN(); |
| zone_owner_->TryLogThermalDebugMessage(absl::StrFormat( |
| "DFF controller %s has fetched no valid input sensor reading.\n", |
| GetId())); |
| } else { |
| zone_owner_->TryLogThermalDebugMessage(absl::StrFormat( |
| "DFF controller %s chose the input value %lf\n", GetId(), input_value)); |
| } |
| |
| return input_value; |
| } |
| |
| void DffController::ProcessOutput(double output) { |
| zone_owner_->AddAdditiveSetpoint(GetId(), output, GetAffectedControllerIds()); |
| zone_owner_->TryLogThermalDebugMessage(absl::StrFormat( |
| "DFF controller %s outputs setpoint: %lf\n", GetId(), output)); |
| if (zone_owner_->GetDebugEnabled()) { |
| zone_owner_->AppendSampledData(absl::StrCat(output, ",")); |
| } |
| } |
| |
| nlohmann::json DffController::ToJson() const { |
| nlohmann::json json; |
| json["H1Coefficient"] = dff_loop_->GetCoeffH1(); |
| json["H2Coefficient"] = dff_loop_->GetCoeffH2(); |
| json["H3Coefficient"] = dff_loop_->GetCoeffH3(); |
| json["F1Coefficient"] = dff_loop_->GetCoeffF1(); |
| json["F2Coefficient"] = dff_loop_->GetCoeffF2(); |
| json["F3Coefficient"] = dff_loop_->GetCoeffF3(); |
| json["ReferencePower"] = dff_loop_->GetReferencePower(); |
| json["OutLimitMax"] = dff_loop_->GetOutputLimitMax(); |
| json["OutLimitMin"] = dff_loop_->GetOutputLimitMin(); |
| json["SlewNeg"] = dff_loop_->GetSlewNeg(); |
| json["SlewPos"] = dff_loop_->GetSlewPos(); |
| json["AffectedControllerIds"] = nlohmann::json::array(); |
| for (const std::string& affected_controller_id : affected_controller_ids_) { |
| json["AffectedControllerIds"].push_back(affected_controller_id); |
| } |
| json["Inputs"] = nlohmann::json::array(); |
| for (const std::string& input : input_sensors_) { |
| json["Inputs"].push_back(input); |
| } |
| return json; |
| } |
| |
| } // namespace thermal |
| } // namespace milotic_tlbmc |