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