blob: 798fd575db5eeafd5a89a51955cda82460fceb47 [file]
#include "tlbmc/thermal/zone_manager.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <vector>
#include "absl/base/nullability.h"
#include "absl/log/log.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include <nlohmann/json.hpp>
#include "thermal_config.pb.h"
#include "fru.pb.h"
#include "resource.pb.h"
#include "sensor.pb.h"
#include "tlbmc/sensors/sensor.h"
#include "tlbmc/thermal/controller/controller.h"
#include "tlbmc/thermal/fan_info/fan_info.h"
#include "tlbmc/thermal/sensor_info/sensor_info.h"
#include "tlbmc/thermal/thermal_control_utils.h"
namespace milotic_tlbmc {
namespace thermal {
absl::StatusOr<std::unique_ptr<ZoneManager>> ZoneManager::Create(
const ZoneManagerParameters& params) {
if (params.zone_failsafe_percent < 0 || params.zone_failsafe_percent > 100) {
return absl::InvalidArgumentError(
absl::StrFormat("Zone failsafe percent must be between 0 and 100, "
"but got %f",
params.zone_failsafe_percent));
}
return absl::WrapUnique(new ZoneManager(params));
}
void ZoneManager::AddSetpoint(absl::string_view controller_id,
double setpoint) {
std::string controller_name = std::string(controller_id);
if (IsSetpointAccumulative()) {
// If setpoint is accumulative, we need to remove the prefix.
// Example:
// `Linear_Temp_CPU0` should actually be `Temp_CPU0`.
controller_name = controller_name.substr(controller_name.find('_') + 1);
}
double org_setpoint = GetThermalControllerSetpoint(controller_name);
if (IsSetpointAccumulative()) {
// Add up the setpoints.
SetThermalControllerSetpoint(controller_name, org_setpoint + setpoint);
} else if (org_setpoint < setpoint) {
// If not accumulative, only update zone manager's setpoint if the current
// setpoint is smaller than the new setpoint.
SetThermalControllerSetpoint(controller_name, setpoint);
}
// If there are multiple thermal controllers with the same value, pick the
// first one as the representative thermal controller, where we can use less
// than, instead of less than or equal to, in order to avoid ineffective
// setpoint updates.
double current_setpoint = GetThermalControllerSetpoint(controller_name);
if (GetMaximumSetpointValue() < current_setpoint) {
SetMaximumSetpoint(controller_name, current_setpoint);
}
}
void ZoneManager::AddAdditiveSetpoint(
absl::string_view controller_id, double setpoint,
const std::vector<std::string>& affected_controller_ids) {
// Ignore non-positive setpoints.
if (setpoint <= 0) {
return;
}
for (const std::string& affected_controller_id : affected_controller_ids) {
const double new_setpoint =
GetThermalControllerSetpoint(affected_controller_id) + setpoint;
SetThermalControllerSetpoint(affected_controller_id, new_setpoint);
if (GetMaximumSetpointValue() < new_setpoint) {
SetMaximumSetpoint(controller_id, new_setpoint);
}
}
}
void ZoneManager::ClearSetpoints() {
maximum_setpoint_info_.Clear();
controller_info_.Clear();
rpm_ceilings_.Clear();
}
void ZoneManager::AdjustSetpoint(double offset) {
for (ThermalController* controller : GetAllThermalControllers()) {
controller->AdjustSetpoint(offset);
}
}
void ZoneManager::DetermineMaximumSetpoint() {
SetpointInfo maximum_setpoint_info = GetMaximumSetpointInfo();
SetpointInfo org_maximum_setpoint_info = maximum_setpoint_info;
maximum_setpoint_info.setpoint_value =
std::clamp(maximum_setpoint_info.setpoint_value, setpoint_lower_bound_,
setpoint_upper_bound_);
double minimum_rpm_ceiling = GetMinimumRpmCeiling();
if (maximum_setpoint_info.setpoint_value > minimum_rpm_ceiling) {
maximum_setpoint_info.sensor_name =
absl::StrCat(GetId(), "_min_rpm_ceiling");
maximum_setpoint_info.setpoint_value = minimum_rpm_ceiling;
}
// Decompose the accumulative setpoint name into the underlying setpoint
// names.
// E.g., `TEMP_C` may become `"PID_TEMP_C+Stepwise_TEMP_C"`
if (IsSetpointAccumulative()) {
std::string accumulative_setpoint_name = maximum_setpoint_info.sensor_name;
std::string combined_setpoint_name;
for (const std::string& controller_name : GetControllerNames()) {
if (!absl::StrContains(controller_name, accumulative_setpoint_name)) {
continue;
}
if (combined_setpoint_name.empty()) {
combined_setpoint_name = controller_name;
continue;
}
absl::StrAppend(&combined_setpoint_name, "+", controller_name);
}
maximum_setpoint_info.sensor_name = combined_setpoint_name;
}
double minimum_setpoint = GetMinimumThermalSetpoint();
if (maximum_setpoint_info.setpoint_value < minimum_setpoint) {
maximum_setpoint_info.setpoint_value = minimum_setpoint;
maximum_setpoint_info.sensor_name = absl::StrCat(GetId(), "_min_setpoint");
}
if (maximum_setpoint_info.setpoint_value !=
org_maximum_setpoint_info.setpoint_value ||
maximum_setpoint_info.sensor_name !=
org_maximum_setpoint_info.sensor_name) {
maximum_setpoint_info_.setpoint_info = maximum_setpoint_info;
}
TryLogThermalDebugMessage(absl::StrFormat(
"Zone %d determines maximum setpoint as %f, requested by %s", GetId(),
maximum_setpoint_info.setpoint_value, maximum_setpoint_info.sensor_name));
}
void ZoneManager::InitializeThermalStates() {
ClearSetpoints();
for (const std::string& sensor_name : GetSensorNames()) {
SetSensorValues(sensor_name, kDefaultSensorValue);
// If the sensor is missing acceptable, avoid entering failsafe
// mode if it is missing.
if (!IsSensorMissingAcceptable(sensor_name)) {
AddFailsafePercent(sensor_name,
sensor_info_.GetFailsafePercent(sensor_name));
}
}
}
void ZoneManager::ProcessThermalControllers() {
// Process non-supportive thermal loops first.
for (ThermalController* controller : GetAllThermalControllers()) {
// Exclude disabled thermal loops from the setpoint calculation.
if (!IsControllerEnabled(controller->GetId()) ||
controller->IsSupportiveController()) {
continue;
}
controller->ProcessThermalLoop();
}
// Process supportive thermal loops in the second pass.
for (ThermalController* controller : GetAllThermalControllers()) {
// Exclude disabled thermal loops from the setpoint calculation.
if (!IsControllerEnabled(controller->GetId()) ||
!controller->IsSupportiveController()) {
continue;
}
controller->ProcessThermalLoop();
}
}
void ZoneManager::ProcessFanControllers() {
for (ThermalController* controller : GetAllFanControllers()) {
controller->ProcessThermalLoop();
}
}
void ZoneManager::LogThermalMessage(absl::string_view message) {
LOG(WARNING) << "[Thermal]: " << message << "\n";
}
void ZoneManager::TryLogThermalDebugMessage(absl::string_view message) const {
if (GetDebugEnabled() || GetDebugPidEnabled()) {
LOG(WARNING) << "[Thermal Debug]: " << message << "\n";
}
}
void ZoneManager::AddThermalController(
absl::string_view controller_name,
ThermalController* absl_nonnull controller, bool is_enabled) {
controller_info_.AddThermalController(controller_name, controller,
is_enabled);
}
std::vector<std::string> ZoneManager::GetControllerNames() {
return controller_info_.GetControllerNames(
(GetTuningEnabled() ? ThreadSafetyMode::EnforceThreadSafety
: ThreadSafetyMode::DisableThreadSafety));
}
void ZoneManager::SetThermalControllerEnabled(absl::string_view controller_name,
bool is_enabled) {
controller_info_.SetThermalControllerEnabled(controller_name, is_enabled);
}
bool ZoneManager::IsControllerEnabled(absl::string_view controller_name) {
return controller_info_.IsControllerEnabled(
controller_name,
(GetTuningEnabled() ? ThreadSafetyMode::EnforceThreadSafety
: ThreadSafetyMode::DisableThreadSafety));
}
std::vector<ThermalController*> ZoneManager::GetAllThermalControllers() {
return controller_info_.GetAllThermalControllers(
(GetTuningEnabled() ? ThreadSafetyMode::EnforceThreadSafety
: ThreadSafetyMode::DisableThreadSafety));
}
std::vector<ThermalController*> ZoneManager::GetAllFanControllers() {
return controller_info_.GetAllFanControllers(
(GetTuningEnabled() ? ThreadSafetyMode::EnforceThreadSafety
: ThreadSafetyMode::DisableThreadSafety));
}
void ZoneManager::SetThermalControllerSetpoint(
absl::string_view controller_name, double setpoint) {
controller_info_.SetThermalControllerSetpoint(controller_name, setpoint);
}
double ZoneManager::GetThermalControllerSetpoint(
absl::string_view controller_name) {
return controller_info_.GetThermalControllerSetpoint(controller_name);
}
void ZoneManager::AddSensor(const ZoneManagerSensorInfo& sensor_info) {
sensor_info_.AddSensor(
sensor_info.sensor_key(),
{
.is_missing_acceptable = (sensor_info.has_is_missing_acceptable()
? sensor_info.is_missing_acceptable()
: false),
.scale = (sensor_info.has_scale() ? sensor_info.scale() : 0),
.failsafe_percent = (sensor_info.has_failsafe_percent()
? sensor_info.failsafe_percent()
: 100),
.critical_thresholds =
{.max_threshold_critical =
(sensor_info.has_max_threshold_critical()
? sensor_info.max_threshold_critical()
: std::numeric_limits<double>::max()),
.min_threshold_critical =
(sensor_info.has_min_threshold_critical()
? sensor_info.min_threshold_critical()
: std::numeric_limits<double>::lowest())},
});
{
absl::MutexLock lock(mutex_);
sensor_names_.push_back(std::string(sensor_info.sensor_key()));
}
}
const std::vector<std::string>& ZoneManager::GetSensorNames() const {
return sensor_names_;
}
void ZoneManager::SetSensorValues(absl::string_view sensor_name,
double sensor_reading) {
sensor_info_.SetSensorValues(
sensor_name, sensor_reading,
(GetTuningEnabled() ? ThreadSafetyMode::EnforceThreadSafety
: ThreadSafetyMode::DisableThreadSafety));
}
SensorReadingInfo ZoneManager::ExtractSensorReadingInfo(
absl::string_view sensor_name, const Sensor* sensor) {
// Sensor state checks.
if (sensor == nullptr) {
if (!IsSensorMissingAcceptable(sensor_name)) {
return SensorReadingInfo(true, "Sensor is missing");
}
return SensorReadingInfo(false, "");
}
// Check whether the sensor state is ready, in case it is associated with a
// pluggable device.
SensorAttributesDynamic sensor_attributes_dynamic =
sensor->GetSensorAttributesDynamic();
if (sensor_attributes_dynamic.has_state() &&
sensor_attributes_dynamic.state().has_status()) {
Status sensor_status = sensor_attributes_dynamic.state().status();
bool is_board_present =
entity_config_
->GetFruTopologyByConfig(sensor->GetSensorAttributesStatic()
.entity_common_config()
.board_config_key())
.ok();
// If the sensor is missing (e.g., not ready, based on
// https://source.corp.google.com/piper///depot/google3/third_party/milotic/external/cc/tlbmc/resource/resource.proto;l=27-43;rcl=820787927),
// or the board does not present, skip processing readings of this sensor.
if (sensor_status != STATUS_READY || !is_board_present) {
// The board presents, while the sensor is missing; and trigger the
// failsafe mode if the sensor is not missing acceptable.
if (!is_board_present) {
return SensorReadingInfo(
false, "Sensor is missing, but its board is missing too");
}
if (!IsSensorMissingAcceptable(sensor_name)) {
return SensorReadingInfo(true,
"Sensor is missing while its board exists");
}
return SensorReadingInfo(
false, "Sensor is missing, but it is marked as acceptable");
}
}
// Sensor reading state checks.
auto sensor_data = sensor->GetSensorData();
if (sensor_data == nullptr || !sensor_data->has_reading() ||
!std::isfinite(sensor_data->reading())) {
return SensorReadingInfo(true, "Sensor has no valid reading");
}
// Sensor reading threshold checks.
double sensor_reading = sensor->GetSensorData()->reading();
SensorThresholds critical_thresholds =
sensor_info_.GetCriticalThresholds(sensor_name);
if (sensor_reading > critical_thresholds.max_threshold_critical) {
return SensorReadingInfo(
true, sensor_reading,
"Sensor reading is above maximal critical threshold");
}
if (sensor_reading < critical_thresholds.min_threshold_critical) {
return SensorReadingInfo(
true, sensor_reading,
"Sensor reading is below minimal critical threshold");
}
return SensorReadingInfo(false, sensor_reading, "");
}
SensorReadingInfo ZoneManager::ExtractTachSensorReadingInfoAndCheckFailsafe(
absl::string_view fan_tach_name, const Sensor* sensor) {
if (sensor == nullptr) {
return ExtractSensorReadingInfo(fan_tach_name, sensor);
}
std::string fan_fru_key =
sensor->GetSensorAttributesStatic().related_item().id();
absl::StatusOr<const milotic_tlbmc::Fru*> source_fan =
entity_config_->GetFru(fan_fru_key);
// TODO - b/500092503: Should we simply return a failsafe mode here?
if (!source_fan.ok()) {
return ExtractSensorReadingInfo(fan_tach_name, sensor);
}
std::string pwm_sensor_name =
(*source_fan)->data().fan_info().pwm_sensor_name();
absl::StatusOr<std::vector<const Fru*>> fans =
entity_config_->GetFansByPwmSensor(pwm_sensor_name);
if (!fans.ok() || fans->size() <= 1) {
return ExtractSensorReadingInfo(fan_tach_name, sensor);
}
bool all_failed = true;
std::string combined_fail_reason;
std::optional<double> current_sensor_reading = std::nullopt;
for (const Fru* fan : *fans) {
std::string tach_name = fan->data().fan_info().tach_sensor_name();
std::shared_ptr<const Sensor> sensor =
aggregator_->GetSensorBySensorKey(tach_name);
SensorReadingInfo failsafe_info =
ExtractSensorReadingInfo(tach_name, sensor.get());
if (tach_name == fan_tach_name) {
current_sensor_reading = failsafe_info.sensor_reading;
}
all_failed &= failsafe_info.trigger_failsafe;
if (!failsafe_info.fail_reason.empty()) {
combined_fail_reason +=
absl::StrFormat("%s: %s", tach_name, failsafe_info.fail_reason);
}
}
return SensorReadingInfo(all_failed, current_sensor_reading,
combined_fail_reason);
}
void ZoneManager::UpdateAllSensorValues() {
bool original_failsafe_mode = GetFailsafeMode();
for (const std::string& sensor_name : sensor_names_) {
std::shared_ptr<const Sensor> sensor =
aggregator_->GetSensorBySensorKey(sensor_name);
SensorReadingInfo sensor_reading_info =
absl::StartsWith(sensor_name, "fantach_")
? ExtractTachSensorReadingInfoAndCheckFailsafe(sensor_name,
sensor.get())
: ExtractSensorReadingInfo(sensor_name, sensor.get());
// Update the sensor reading for the local record table and the sampled data
// log.
std::optional<double> sensor_reading = sensor_reading_info.sensor_reading;
if (sensor_reading.has_value()) {
SetSensorValues(sensor_name, sensor_reading.value());
}
if (GetDebugEnabled()) {
AppendSampledData((sensor_reading.has_value()
? absl::StrCat(sensor_reading.value(), ",")
: "NaN,"));
}
// Trigger failsafe mode if necessary.
if (sensor_reading_info.trigger_failsafe) {
AddFailsafePercent(sensor_name,
sensor_info_.GetFailsafePercent(sensor_name));
OutputFailsafeLog(sensor_name, sensor_reading_info.fail_reason);
continue;
}
// In case the sensor is failing but not enough to trigger the failsafe
// mode, we still need to output the failsafe log.
if (!sensor_reading_info.fail_reason.empty()) {
OutputFailsafeLog(sensor_name, sensor_reading_info.fail_reason);
}
// If the sensor has no valid reading, continue.
if (!sensor_reading_info.sensor_reading) {
continue;
}
// Remove the failsafe state as the sensor is fully recovered.
RemoveFailsafeStateForDevice(sensor_name);
}
if (original_failsafe_mode && GetFailsafeMode() != original_failsafe_mode) {
OutputFailsafeLog(absl::StrCat("zone_", GetId()),
"All sensors are recovered.");
}
}
SensorValues ZoneManager::GetSensorReadings(absl::string_view sensor_name) {
return sensor_info_.GetSensorValues(
sensor_name,
(GetTuningEnabled() ? ThreadSafetyMode::EnforceThreadSafety
: ThreadSafetyMode::DisableThreadSafety));
}
absl::StatusOr<const SensorMetadata&> ZoneManager::GetSensorMetadata(
absl::string_view sensor_name) {
return sensor_info_.GetSensorMetadata(sensor_name);
}
bool ZoneManager::IsSensorMissingAcceptable(absl::string_view sensor_name) {
return sensor_info_.IsSensorMissingAcceptable(sensor_name);
}
void ZoneManager::AddFan(const ZoneManagerFanInfo& fan_info) {
fan_info_.AddFan(fan_info.fan_name(),
{
.failsafe_percent = (fan_info.has_failsafe_percent()
? fan_info.failsafe_percent()
: 100),
.critical_thresholds =
{.max_threshold_critical =
(fan_info.has_max_threshold_critical()
? fan_info.max_threshold_critical()
: std::numeric_limits<double>::max()),
.min_threshold_critical =
(fan_info.has_min_threshold_critical()
? fan_info.min_threshold_critical()
: std::numeric_limits<double>::lowest())},
});
{
absl::MutexLock lock(mutex_);
fan_names_.push_back(fan_info.fan_name());
}
}
const std::vector<std::string>& ZoneManager::GetFanNames() const {
return fan_names_;
}
void ZoneManager::UpdateLocallyRecordedFanPwmAndCheckFailsafe(
absl::string_view fan_name, double pwm) {
std::optional<double> failsafe_pwm =
fan_info_.UpdateLocallyRecordedFanPwmAndCheckFailsafe(
fan_name, pwm,
(GetTuningEnabled() ? ThreadSafetyMode::EnforceThreadSafety
: ThreadSafetyMode::DisableThreadSafety));
if (failsafe_pwm) {
AddFailsafePercent(fan_name, *failsafe_pwm);
OutputFailsafeLog(
fan_name,
absl::StrFormat("Fan PWM %f is beyond its critical thresholds", pwm));
}
}
absl::StatusOr<const FanMetadata&> ZoneManager::GetFanMetadata(
absl::string_view fan_name) {
return fan_info_.GetFanMetadata(fan_name);
}
double ZoneManager::GetFanPwm(absl::string_view fan_name) {
return fan_info_.GetFanPwm(
fan_name, (GetTuningEnabled() ? ThreadSafetyMode::EnforceThreadSafety
: ThreadSafetyMode::DisableThreadSafety));
}
void ZoneManager::WriteFanSpeed(absl::string_view fan_name, double pwm) {
pwm = std::max(pwm, 0.0);
SensorValue sensor_value_pwm;
sensor_value_pwm.set_reading(pwm);
if (!aggregator_->WriteToSensor(std::string(fan_name), sensor_value_pwm)
.ok()) {
LOG(ERROR) << "[Thermal]: Failed to write PWM for fan " << fan_name;
return;
}
TryLogThermalDebugMessage(absl::StrFormat("Zone %d writes PWM %f to fan %s",
GetId(), pwm, fan_name));
}
void ZoneManager::WriteFanSpeeds() {
// If failsafe mode is triggered by fan controllers, the failsafe PWM may
// not be applied to all the fans yet.
double failsafe_percent = GetFailsafePercent();
for (const std::string& fan_name : GetFanNames()) {
WriteFanSpeed(fan_name, std::max(GetFanPwm(fan_name), failsafe_percent));
}
if (GetFailsafeMode()) {
for (const std::string& fan_name : GetFanNames()) {
RemoveFailsafeStateForDevice(fan_name);
}
}
}
bool ZoneManager::GetFailsafeMode() const {
return failsafe_device_info_.AnyDeviceInFailsafeMode();
}
double ZoneManager::GetFailsafePercent() {
if (!GetFailsafeMode()) {
return 0;
}
return std::max(zone_failsafe_percent_,
failsafe_device_info_.GetMaxFailsafePercent());
}
void ZoneManager::AddFailsafePercent(absl::string_view device_name,
double failsafe_percent) {
failsafe_device_info_.AddFailsafePercent(device_name, failsafe_percent);
if (GetManualMode()) {
SetManualMode(false);
LogThermalMessage(absl::StrFormat(
"Zone %d adds failsafe percent, manual mode is set to false.",
GetId()));
}
}
void ZoneManager::RemoveFailsafeStateForDevice(absl::string_view device_name) {
failsafe_device_info_.RemoveFailsafeStateForDevice(device_name);
}
void ZoneManager::OutputFailsafeLog(absl::string_view location,
absl::string_view reason) {
failsafe_logger_.OutputFailsafeLog(
GetId(),
(GetFailsafeMode() ? FailsafeState::Failsafe
: FailsafeState::NonFailsafe),
location, reason);
}
void ZoneManager::SetManualPwm(absl::string_view fan_name, double pwm) {
absl::MutexLock lock(mutex_);
fan_name_to_manual_pwm_[fan_name] = pwm;
}
void ZoneManager::WriteCurrentPwmToFans() {
for (const std::string& fan_name : GetFanNames()) {
{
absl::MutexLock lock(mutex_);
auto it = fan_name_to_manual_pwm_.find(fan_name);
if (it != fan_name_to_manual_pwm_.end()) {
WriteFanSpeed(fan_name, it->second);
continue;
}
}
std::shared_ptr<const Sensor> pwm_sensor =
aggregator_->GetSensorBySensorKey(fan_name);
if (pwm_sensor == nullptr || pwm_sensor->GetSensorData() == nullptr ||
!pwm_sensor->GetSensorData()->has_reading()) {
continue;
}
double pwm = pwm_sensor->GetSensorData()->reading();
WriteFanSpeed(fan_name, pwm);
}
}
void ZoneManager::SetMaximumSetpoint(absl::string_view setpoint_sensor_name,
double setpoint) {
// Maximum setpoint and its (previous) name must be set together.
maximum_setpoint_info_.setpoint_info.setpoint_value = setpoint;
maximum_setpoint_info_.setpoint_info.previous_sensor_name =
maximum_setpoint_info_.setpoint_info.sensor_name;
maximum_setpoint_info_.setpoint_info.sensor_name = setpoint_sensor_name;
}
void ZoneManager::DumpSampledData() {
if (!is_sampled_data_header_written_) {
DumpSampledDataHeader();
is_sampled_data_header_written_ = true;
}
absl::MutexLock lock(mutex_);
std::string data_to_dump = absl::StrCat(absl::Now(), ",", sampled_data_);
sampled_data_.clear();
// Output the log anyway, in case the file fails to be written.
LOG(WARNING) << "[Thermal Sampled Data]: " << data_to_dump;
if (!sample_data_file_.is_open()) {
LOG(ERROR) << "[Thermal]: Failed to open sampled data file: "
<< sample_data_file_path_;
return;
}
sample_data_file_ << data_to_dump << "\n" << std::flush;
}
void ZoneManager::DumpSampledDataHeader() {
absl::MutexLock lock(mutex_);
std::string header = "Timestamp,";
for (const std::string& sensor_name : sensor_names_) {
absl::StrAppend(&header, sensor_name, ",");
}
for (const std::string& controller_name : GetControllerNames()) {
const ThermalController* controller =
controller_info_.GetThermalController(controller_name);
// Dump non-supportive thermal controllers first.
if (controller == nullptr || controller->IsSupportiveController() ||
controller->IsFanController() ||
!IsControllerEnabled(controller_name)) {
continue;
}
absl::StrAppend(&header, controller->GetId(), ",");
}
for (const std::string& controller_name : GetControllerNames()) {
const ThermalController* controller =
controller_info_.GetThermalController(controller_name);
if (controller == nullptr || !controller->IsSupportiveController() ||
!IsControllerEnabled(controller_name)) {
continue;
}
absl::StrAppend(&header, controller->GetId(), ",");
}
for (const std::string& controller_name : GetControllerNames()) {
const ThermalController* controller =
controller_info_.GetThermalController(controller_name);
if (controller == nullptr || !controller->IsFanController()) {
continue;
}
absl::StrAppend(&header, controller->GetId(), ",");
}
LOG(WARNING) << "[Thermal Sampled Data Header]: " << header;
if (!sample_data_file_.is_open()) {
LOG(ERROR) << "[Thermal]: Failed to open sampled data file: "
<< sample_data_file_path_;
return;
}
sample_data_file_ << header << "\n" << std::flush;
}
nlohmann::json ZoneManager::ToJson() const {
nlohmann::json json;
json["Id"] = GetId();
json["SensorNames"] = GetSensorNames();
json["FanNames"] = GetFanNames();
json["FailSafe"] = (GetFailsafeMode() ? "Active" : "Normal");
return json;
}
} // namespace thermal
} // namespace milotic_tlbmc