blob: 463fb31a87b655566667fe9f0e1f84178e980701 [file]
#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