| #include "tlbmc/collector/thermal_collector.h" |
| |
| #include <algorithm> |
| #include <atomic> |
| #include <chrono> // NOLINT |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/base/nullability.h" |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/log/log.h" |
| #include "absl/memory/memory.h" |
| #include "absl/status/status.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 "g3/macros.h" |
| #include "thread/thread.h" |
| #include <nlohmann/json_fwd.hpp> |
| #include "tlbmc/collector/fru_collector.h" |
| #include "tlbmc/collector/sensor_collector_aggregator.h" |
| #include "tlbmc/configs/entity_config.h" |
| #include "thermal_config.pb.h" |
| #include "tlbmc/thermal/controller/algorithm/dff.h" |
| #include "tlbmc/thermal/controller/algorithm/eat.h" |
| #include "tlbmc/thermal/controller/algorithm/first_order_adrc.h" |
| #include "tlbmc/thermal/controller/algorithm/pid.h" |
| #include "tlbmc/thermal/controller/algorithm/second_order_adrc.h" |
| #include "tlbmc/thermal/controller/algorithm/stepwise.h" |
| #include "tlbmc/thermal/controller/controller.h" |
| #include "tlbmc/thermal/controller/dff_controller.h" |
| #include "tlbmc/thermal/controller/eat_controller.h" |
| #include "tlbmc/thermal/controller/fan_pid_controller.h" |
| #include "tlbmc/thermal/controller/first_order_adrc_controller.h" |
| #include "tlbmc/thermal/controller/optimizer/pid/frequency_response_estimation/frequency_response_estimation.h" |
| #include "tlbmc/thermal/controller/optimizer/pid/pid_optimizer.h" |
| #include "tlbmc/thermal/controller/optimizer/pid/ziegler_nichols/differentiated_ziegler_nichols.h" |
| #include "tlbmc/thermal/controller/optimizer/pid/ziegler_nichols/ziegler_nichols.h" |
| #include "tlbmc/thermal/controller/pid_controller.h" |
| #include "tlbmc/thermal/controller/second_order_adrc_controller.h" |
| #include "tlbmc/thermal/controller/stepwise_controller.h" |
| #include "tlbmc/thermal/debug_mode.h" |
| #include "tlbmc/thermal/zone_manager.h" |
| |
| namespace milotic_tlbmc { |
| |
| absl::flat_hash_map<std::string, thermal::FirstOrderAdrcController*> |
| ThermalCollector:: |
| LinkFirstOrderAdrcControllerToZoneAndCreateIdToControllerMap() { |
| absl::flat_hash_map<std::string, thermal::FirstOrderAdrcController*> |
| controller_id_to_first_order_adrc_controller; |
| for (const auto& [zone_id_and_controller_id, first_order_adrc_controller] : |
| zone_id_and_controller_id_to_first_order_adrc_controller_) { |
| const auto& [zone_id, controller_id] = zone_id_and_controller_id; |
| first_order_adrc_controller->GetZoneManager()->AddThermalController( |
| controller_id, first_order_adrc_controller.get()); |
| |
| LOG(WARNING) << absl::StrFormat( |
| "1st-order ADRC controller %s linked to zone %d", controller_id, |
| zone_id); |
| |
| controller_id_to_first_order_adrc_controller.insert( |
| {controller_id, first_order_adrc_controller.get()}); |
| } |
| return controller_id_to_first_order_adrc_controller; |
| } |
| |
| absl::flat_hash_map<std::string, thermal::SecondOrderAdrcController*> |
| ThermalCollector:: |
| LinkSecondOrderAdrcControllerToZoneAndCreateIdToControllerMap() { |
| absl::flat_hash_map<std::string, thermal::SecondOrderAdrcController*> |
| controller_id_to_second_order_adrc_controller; |
| for (const auto& [zone_id_and_controller_id, second_order_adrc_controller] : |
| zone_id_and_controller_id_to_second_order_adrc_controller_) { |
| const auto& [zone_id, controller_id] = zone_id_and_controller_id; |
| second_order_adrc_controller->GetZoneManager()->AddThermalController( |
| controller_id, second_order_adrc_controller.get()); |
| |
| LOG(WARNING) << absl::StrFormat( |
| "2nd-order ADRC controller %s linked to zone %d", controller_id, |
| zone_id); |
| |
| controller_id_to_second_order_adrc_controller.insert( |
| {controller_id, second_order_adrc_controller.get()}); |
| } |
| return controller_id_to_second_order_adrc_controller; |
| } |
| |
| absl::flat_hash_map<std::string, thermal::PidController*> |
| ThermalCollector::LinkPidControllerToZoneAndCreateIdToControllerMap() { |
| absl::flat_hash_map<std::string, thermal::PidController*> |
| controller_id_to_pid_controller; |
| for (const auto& [zone_id_and_controller_id, pid_controller] : |
| zone_id_and_controller_id_to_pid_controller_) { |
| const auto& [zone_id, controller_id] = zone_id_and_controller_id; |
| pid_controller->GetZoneManager()->AddThermalController( |
| controller_id, pid_controller.get()); |
| |
| LOG(WARNING) << absl::StrFormat("PID controller %s linked to zone %d", |
| controller_id, zone_id); |
| |
| controller_id_to_pid_controller.insert( |
| {controller_id, pid_controller.get()}); |
| } |
| return controller_id_to_pid_controller; |
| } |
| |
| absl::flat_hash_map<std::string, thermal::StepwiseController*> |
| ThermalCollector::LinkStepwiseControllerToZoneAndCreateIdToControllerMap() { |
| absl::flat_hash_map<std::string, thermal::StepwiseController*> |
| controller_id_to_stepwise_controller; |
| for (const auto& [zone_id_and_controller_id, stepwise_controller] : |
| zone_id_and_controller_id_to_stepwise_controller_) { |
| const auto& [zone_id, controller_id] = zone_id_and_controller_id; |
| stepwise_controller->GetZoneManager()->AddThermalController( |
| controller_id, stepwise_controller.get()); |
| |
| LOG(WARNING) << absl::StrFormat("Stepwise controller %s linked to zone %d", |
| controller_id, zone_id); |
| |
| controller_id_to_stepwise_controller.insert( |
| {controller_id, stepwise_controller.get()}); |
| } |
| return controller_id_to_stepwise_controller; |
| } |
| |
| absl::flat_hash_map<std::string, thermal::FanPidController*> |
| ThermalCollector::LinkFanPidControllerToZoneAndCreateIdToControllerMap() { |
| absl::flat_hash_map<std::string, thermal::FanPidController*> |
| controller_id_to_fan_pid_controller; |
| for (const auto& [zone_id_and_controller_id, fan_pid_controller] : |
| zone_id_and_controller_id_to_fan_pid_controller_) { |
| const auto& [zone_id, controller_id] = zone_id_and_controller_id; |
| fan_pid_controller->GetZoneManager()->AddThermalController( |
| controller_id, fan_pid_controller.get()); |
| |
| LOG(WARNING) << absl::StrFormat("Fan PID controller %s linked to zone %d", |
| controller_id, zone_id); |
| |
| controller_id_to_fan_pid_controller.insert( |
| {controller_id, fan_pid_controller.get()}); |
| } |
| return controller_id_to_fan_pid_controller; |
| } |
| |
| absl::flat_hash_map<std::string, thermal::DffController*> |
| ThermalCollector::LinkDffControllerToZoneAndCreateIdToControllerMap() { |
| absl::flat_hash_map<std::string, thermal::DffController*> |
| controller_id_to_dff_controller; |
| for (const auto& [zone_id_and_controller_id, dff_controller] : |
| zone_id_and_controller_id_to_dff_controller_) { |
| const auto& [zone_id, controller_id] = zone_id_and_controller_id; |
| dff_controller->GetZoneManager()->AddThermalController( |
| controller_id, dff_controller.get()); |
| |
| LOG(WARNING) << absl::StrFormat("DFF controller %s linked to zone %d", |
| controller_id, zone_id); |
| |
| controller_id_to_dff_controller.insert( |
| {controller_id, dff_controller.get()}); |
| } |
| return controller_id_to_dff_controller; |
| } |
| |
| absl::flat_hash_map<std::string, thermal::EatController*> |
| ThermalCollector::LinkEatControllerToZoneAndCreateIdToControllerMap() { |
| absl::flat_hash_map<std::string, thermal::EatController*> |
| controller_id_to_eat_controller; |
| for (const auto& [zone_id_and_controller_id, eat_controller] : |
| zone_id_and_controller_id_to_eat_controller_) { |
| const auto& [zone_id, controller_id] = zone_id_and_controller_id; |
| eat_controller->GetZoneManager()->AddThermalController( |
| controller_id, eat_controller.get()); |
| |
| std::vector<thermal::ZoneManager*> affected_zones; |
| affected_zones.reserve(eat_controller->GetAffectedZoneIds().size()); |
| for (int affected_zone_id : eat_controller->GetAffectedZoneIds()) { |
| auto zone_manager = zone_id_to_zone_manager_.find(affected_zone_id); |
| if (zone_manager == zone_id_to_zone_manager_.end()) { |
| LOG(ERROR) << absl::StrFormat("Zone %d not found for EAT controller %s", |
| affected_zone_id, controller_id); |
| continue; |
| } |
| affected_zones.push_back(zone_manager->second.get()); |
| } |
| if (!eat_controller->SetAffectedZones(std::move(affected_zones)).ok()) { |
| LOG(ERROR) << absl::StrFormat( |
| "Failed to set affected zones for EAT controller %s", controller_id); |
| } |
| |
| LOG(WARNING) << absl::StrFormat("EAT controller %s linked to zone %d", |
| controller_id, zone_id); |
| |
| controller_id_to_eat_controller.insert( |
| {controller_id, eat_controller.get()}); |
| } |
| return controller_id_to_eat_controller; |
| } |
| |
| ThermalCollector::ThermalCollector( |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::FirstOrderAdrcController>>&& |
| zone_id_and_controller_id_to_first_order_adrc_controller, |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::SecondOrderAdrcController>>&& |
| zone_id_and_controller_id_to_second_order_adrc_controller, |
| absl::flat_hash_map<int, std::unique_ptr<thermal::ZoneManager>>&& |
| zone_id_to_zone_manager, |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::PidController>>&& |
| zone_id_and_controller_id_to_pid_controller, |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::StepwiseController>>&& |
| zone_id_and_controller_id_to_stepwise_controller, |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::FanPidController>>&& |
| zone_id_and_controller_id_to_fan_pid_controller, |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::DffController>>&& |
| zone_id_and_controller_id_to_dff_controller, |
| absl::flat_hash_map<std::string, std::vector<int>>&& |
| controller_id_to_zone_ids, |
| absl::flat_hash_map<int, std::unique_ptr<thermal::ZoneManager>>&& |
| zone_id_to_supportive_zone_manager, |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::EatController>>&& |
| zone_id_and_controller_id_to_eat_controller, |
| std::unique_ptr<ThermalThreadManager> thermal_thread_manager, |
| ecclesia::ThreadFactoryInterface* thread_factory) |
| : zone_id_to_zone_manager_(std::move(zone_id_to_zone_manager)), |
| zone_id_and_controller_id_to_first_order_adrc_controller_( |
| std::move(zone_id_and_controller_id_to_first_order_adrc_controller)), |
| zone_id_and_controller_id_to_second_order_adrc_controller_( |
| std::move(zone_id_and_controller_id_to_second_order_adrc_controller)), |
| zone_id_and_controller_id_to_pid_controller_( |
| std::move(zone_id_and_controller_id_to_pid_controller)), |
| zone_id_and_controller_id_to_stepwise_controller_( |
| std::move(zone_id_and_controller_id_to_stepwise_controller)), |
| zone_id_and_controller_id_to_fan_pid_controller_( |
| std::move(zone_id_and_controller_id_to_fan_pid_controller)), |
| zone_id_and_controller_id_to_dff_controller_( |
| std::move(zone_id_and_controller_id_to_dff_controller)), |
| controller_id_to_zone_ids_(std::move(controller_id_to_zone_ids)), |
| zone_id_to_supportive_zone_manager_( |
| std::move(zone_id_to_supportive_zone_manager)), |
| zone_id_and_controller_id_to_eat_controller_( |
| std::move(zone_id_and_controller_id_to_eat_controller)), |
| // Link thermal controllers to their zone managers. |
| // Initialize maps for ControllerIdToController. |
| controller_id_to_controller_({ |
| .controller_id_to_first_order_adrc_controller = |
| LinkFirstOrderAdrcControllerToZoneAndCreateIdToControllerMap(), |
| .controller_id_to_second_order_adrc_controller = |
| LinkSecondOrderAdrcControllerToZoneAndCreateIdToControllerMap(), |
| .controller_id_to_pid_controller = |
| LinkPidControllerToZoneAndCreateIdToControllerMap(), |
| .controller_id_to_stepwise_controller = |
| LinkStepwiseControllerToZoneAndCreateIdToControllerMap(), |
| .controller_id_to_fan_pid_controller = |
| LinkFanPidControllerToZoneAndCreateIdToControllerMap(), |
| .controller_id_to_dff_controller = |
| LinkDffControllerToZoneAndCreateIdToControllerMap(), |
| .controller_id_to_eat_controller = |
| LinkEatControllerToZoneAndCreateIdToControllerMap(), |
| }), |
| thermal_thread_manager_(std::move(thermal_thread_manager)), |
| thread_factory_(thread_factory) {} |
| |
| ThermalCollector::~ThermalCollector() { |
| StopThermalControl(); |
| |
| // In the case of EmptySensorCollector, avoid dereferencing nullptr. |
| if (thermal_thread_manager_ == nullptr) { |
| return; |
| } |
| |
| thermal_thread_manager_->task_scheduler->Stop(); |
| thermal_thread_manager_->work_guards.clear(); |
| |
| thermal_control_timers_.clear(); |
| |
| for (const std::shared_ptr<boost::asio::io_context>& io_context : |
| thermal_thread_manager_->io_contexts) { |
| io_context->stop(); |
| } |
| |
| for (const std::unique_ptr<ecclesia::ThreadInterface>& thread : |
| thermal_thread_manager_->threads) { |
| thread->Join(); |
| } |
| } |
| |
| absl::StatusOr<std::unique_ptr<ThermalCollector>> ThermalCollector::Create( |
| const Params& params, SensorCollectorAggregator* absl_nonnull aggregator, |
| FruCollector* absl_nonnull fru_collector, |
| EntityConfig* absl_nonnull entity_config) { |
| auto thermal_thread_manager = |
| std::make_unique<ThermalThreadManager>(params.clock); |
| |
| auto io_context = std::make_shared<boost::asio::io_context>(); |
| thermal_thread_manager->io_contexts.push_back(io_context); |
| thermal_thread_manager->work_guards.push_back( |
| boost::asio::make_work_guard(*io_context)); |
| |
| bool tuning_enabled = params.tuning_enabled; |
| uint32_t failsafe_log_count_per_sec = |
| params.failsafe_logger_config.failsafe_log_limit_per_second(); |
| if (failsafe_log_count_per_sec < 1) { |
| failsafe_log_count_per_sec = 20; |
| } |
| std::string sample_data_file_prefix = params.sample_data_file_prefix; |
| |
| absl::flat_hash_map<std::string, std::vector<int>> controller_id_to_zone_ids; |
| |
| // Create zone managers. |
| absl::flat_hash_map<int, std::unique_ptr<thermal::ZoneManager>> |
| zone_id_to_zone_manager; |
| for (const ZoneManagerConfig& zone_config : |
| params.zone_manager_configs.zone_manager_configs()) { |
| ECCLESIA_ASSIGN_OR_RETURN( |
| std::unique_ptr<thermal::ZoneManager> zone_manager, |
| thermal::ZoneManager::Create({ |
| .id = zone_config.id(), |
| .ms_per_fan_cycle = zone_config.ms_per_fan_cycle(), |
| .ms_per_thermal_cycle = zone_config.ms_per_thermal_cycle(), |
| .failsafe_log_count_per_sec = failsafe_log_count_per_sec, |
| .debug_mode = thermal::DebugMode( |
| zone_config.debug_mode().debug_enabled(), |
| zone_config.debug_mode().debug_pid_enabled(), |
| zone_config.debug_mode().debug_dff_enabled(), tuning_enabled), |
| .aggregator = aggregator, |
| .fru_collector = fru_collector, |
| .entity_config = entity_config, |
| .zone_failsafe_percent = zone_config.zone_failsafe_percent(), |
| .setpoint_upper_bound = zone_config.setpoint_upper_bound(), |
| .setpoint_lower_bound = zone_config.setpoint_lower_bound(), |
| .minimum_thermal_setpoint = zone_config.minimum_thermal_setpoint(), |
| .input_sensors = {zone_config.input_sensors().begin(), |
| zone_config.input_sensors().end()}, |
| .output_fans = {zone_config.output_fans().begin(), |
| zone_config.output_fans().end()}, |
| .sample_data_file_prefix = sample_data_file_prefix, |
| })); |
| |
| LOG(WARNING) << absl::StrFormat("Zone manager %d created", |
| zone_manager->GetId()); |
| |
| zone_id_to_zone_manager[zone_config.id()] = std::move(zone_manager); |
| } |
| |
| // Create 1st-order ADRC controllers. |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::FirstOrderAdrcController>> |
| zone_id_and_controller_id_to_first_order_adrc_controller; |
| for (const FirstOrderAdrcControllerConfig& adrc_config : |
| params.first_order_adrc_controller_configs |
| .first_order_adrc_controller_configs()) { |
| for (int zone_id : adrc_config.zone_manager_id()) { |
| FirstOrderAdrcLoopConfig adrc_loop_config = |
| adrc_config.first_order_adrc_loop_config(); |
| |
| auto adrc_loop = std::make_unique<thermal::FirstOrderAdrcThermalLoop>( |
| adrc_loop_config); |
| |
| ECCLESIA_ASSIGN_OR_RETURN( |
| std::unique_ptr<thermal::FirstOrderAdrcController> adrc_controller, |
| thermal::FirstOrderAdrcController::Create({ |
| .id = adrc_config.id(), |
| .zone_manager = zone_id_to_zone_manager[zone_id].get(), |
| .first_order_adrc_loop = std::move(adrc_loop), |
| .setpoint = adrc_config.setpoint(), |
| .input_sensors = {adrc_config.input_sensors().begin(), |
| adrc_config.input_sensors().end()}, |
| })); |
| |
| LOG(WARNING) << absl::StrFormat( |
| "1st-order ADRC controller %s created for zone %d", |
| adrc_controller->GetId(), zone_id); |
| |
| controller_id_to_zone_ids[adrc_controller->GetId()].push_back( |
| adrc_controller->GetZoneManager()->GetId()); |
| zone_id_and_controller_id_to_first_order_adrc_controller[std::make_tuple( |
| zone_id, adrc_controller->GetId())] = std::move(adrc_controller); |
| } |
| } |
| |
| // Create 2nd-order ADRC controllers. |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::SecondOrderAdrcController>> |
| zone_id_and_controller_id_to_second_order_adrc_controller; |
| for (const SecondOrderAdrcControllerConfig& adrc_config : |
| params.second_order_adrc_controller_configs |
| .second_order_adrc_controller_configs()) { |
| for (int zone_id : adrc_config.zone_manager_id()) { |
| SecondOrderAdrcLoopConfig adrc_loop_config = |
| adrc_config.second_order_adrc_loop_config(); |
| |
| std::vector<thermal::DisturbanceFactors> disturbance_factors; |
| std::vector<std::vector<std::string>> disturbance_sensor_lists; |
| for (const QuantifiableDisturbance& quantifiable_disturbance : |
| adrc_config.quantifiable_disturbances()) { |
| disturbance_factors.push_back(thermal::DisturbanceFactors{ |
| .d = quantifiable_disturbance.d(), |
| .reference_setpoint = |
| quantifiable_disturbance.reference_setpoint()}); |
| |
| std::vector<std::string> disturbance_sensor_list( |
| quantifiable_disturbance.input_sensors().begin(), |
| quantifiable_disturbance.input_sensors().end()); |
| disturbance_sensor_lists.push_back(disturbance_sensor_list); |
| } |
| |
| auto adrc_loop = std::make_unique<thermal::SecondOrderAdrcThermalLoop>( |
| adrc_loop_config, disturbance_factors); |
| |
| ECCLESIA_ASSIGN_OR_RETURN( |
| std::unique_ptr<thermal::SecondOrderAdrcController> adrc_controller, |
| thermal::SecondOrderAdrcController::Create({ |
| .id = adrc_config.id(), |
| .zone_manager = zone_id_to_zone_manager[zone_id].get(), |
| .second_order_adrc_loop = std::move(adrc_loop), |
| .setpoint = adrc_config.setpoint(), |
| .input_sensors = {adrc_config.input_sensors().begin(), |
| adrc_config.input_sensors().end()}, |
| .disturbance_sensor_lists = std::move(disturbance_sensor_lists), |
| })); |
| |
| LOG(WARNING) << absl::StrFormat( |
| "2nd-order ADRC controller %s created for zone %d", |
| adrc_controller->GetId(), zone_id); |
| |
| controller_id_to_zone_ids[adrc_controller->GetId()].push_back( |
| adrc_controller->GetZoneManager()->GetId()); |
| zone_id_and_controller_id_to_second_order_adrc_controller[std::make_tuple( |
| zone_id, adrc_controller->GetId())] = std::move(adrc_controller); |
| } |
| } |
| |
| // Create PID controllers. |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::PidController>> |
| zone_id_and_controller_id_to_pid_controller; |
| for (const PidControllerConfig& pid_config : |
| params.pid_controller_configs.pid_controller_configs()) { |
| for (int zone_id : pid_config.zone_manager_id()) { |
| auto pid_loop = std::make_unique<thermal::PidThermalLoop>( |
| pid_config.pid_loop_config()); |
| thermal::PidOptimizerInfo pid_optimizer_info; |
| if (pid_config.has_pid_autotuner_config()) { |
| ECCLESIA_ASSIGN_OR_RETURN(thermal::PidOptimizerInfo tuner, |
| CreatePidAutoTuner(pid_config)); |
| pid_optimizer_info = std::move(tuner); |
| } |
| |
| ECCLESIA_ASSIGN_OR_RETURN( |
| std::unique_ptr<thermal::PidController> pid_controller, |
| thermal::PidController::Create({ |
| .id = pid_config.id(), |
| .zone_manager = zone_id_to_zone_manager[zone_id].get(), |
| .pid_loop = std::move(pid_loop), |
| .setpoint = pid_config.setpoint(), |
| .input_process_type = pid_config.input_process_type(), |
| .input_sensors = {pid_config.input_sensors().begin(), |
| pid_config.input_sensors().end()}, |
| .pid_optimizer_info = std::move(pid_optimizer_info), |
| })); |
| |
| LOG(WARNING) << absl::StrFormat("PID controller %s created for zone %d", |
| pid_controller->GetId(), zone_id); |
| |
| controller_id_to_zone_ids[pid_controller->GetId()].push_back( |
| pid_controller->GetZoneManager()->GetId()); |
| zone_id_and_controller_id_to_pid_controller[std::make_tuple( |
| zone_id, pid_controller->GetId())] = std::move(pid_controller); |
| } |
| } |
| |
| // Create stepwise controllers. |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::StepwiseController>> |
| zone_id_and_controller_id_to_stepwise_controller; |
| for (const StepwiseControllerConfig& stepwise_config : |
| params.stepwise_controller_configs.stepwise_controller_configs()) { |
| for (int zone_id : stepwise_config.zone_manager_id()) { |
| auto stepwise_loop = std::make_unique<thermal::StepwiseThermalLoop>( |
| stepwise_config.stepwise_loop_config()); |
| |
| ECCLESIA_ASSIGN_OR_RETURN( |
| std::unique_ptr<thermal::StepwiseController> stepwise_controller, |
| thermal::StepwiseController::Create({ |
| .id = stepwise_config.id(), |
| .zone_manager = zone_id_to_zone_manager[zone_id].get(), |
| .stepwise_loop = std::move(stepwise_loop), |
| .input_sensors = {stepwise_config.input_sensors().begin(), |
| stepwise_config.input_sensors().end()}, |
| .is_ceiling = stepwise_config.is_ceiling(), |
| })); |
| |
| LOG(WARNING) << absl::StrFormat( |
| "Stepwise controller %s created for zone %d", |
| stepwise_controller->GetId(), zone_id); |
| |
| controller_id_to_zone_ids[stepwise_controller->GetId()].push_back( |
| stepwise_controller->GetZoneManager()->GetId()); |
| zone_id_and_controller_id_to_stepwise_controller[std::make_tuple( |
| zone_id, stepwise_controller->GetId())] = |
| std::move(stepwise_controller); |
| } |
| } |
| |
| // Create fan PID controllers. |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::FanPidController>> |
| zone_id_and_controller_id_to_fan_pid_controller; |
| for (const FanPidControllerConfig& fan_pid_config : |
| params.fan_pid_controller_configs.fan_pid_controller_configs()) { |
| for (int zone_id : fan_pid_config.zone_manager_id()) { |
| auto pid_loop = std::make_unique<thermal::PidThermalLoop>( |
| fan_pid_config.pid_loop_config()); |
| |
| ECCLESIA_ASSIGN_OR_RETURN( |
| std::unique_ptr<thermal::FanPidController> fan_pid_controller, |
| thermal::FanPidController::Create({ |
| .id = fan_pid_config.id(), |
| .zone_manager = zone_id_to_zone_manager[zone_id].get(), |
| .pid_loop = std::move(pid_loop), |
| .input_fans = {fan_pid_config.input_fans().begin(), |
| fan_pid_config.input_fans().end()}, |
| .output_fans = {fan_pid_config.output_fans().begin(), |
| fan_pid_config.output_fans().end()}, |
| })); |
| |
| LOG(WARNING) << absl::StrFormat( |
| "Fan PID controller %s created for zone %d", |
| fan_pid_controller->GetId(), zone_id); |
| |
| controller_id_to_zone_ids[fan_pid_controller->GetId()].push_back( |
| fan_pid_controller->GetZoneManager()->GetId()); |
| zone_id_and_controller_id_to_fan_pid_controller[std::make_tuple( |
| zone_id, fan_pid_controller->GetId())] = |
| std::move(fan_pid_controller); |
| } |
| } |
| |
| // Create DFF controllers. |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::DffController>> |
| zone_id_and_controller_id_to_dff_controller; |
| for (const DffControllerConfig& dff_config : |
| params.dff_controller_configs.dff_controller_configs()) { |
| for (int zone_id : dff_config.zone_manager_id()) { |
| auto dff_loop = std::make_unique<thermal::DffThermalLoop>( |
| dff_config.dff_loop_config()); |
| |
| ECCLESIA_ASSIGN_OR_RETURN( |
| std::unique_ptr<thermal::DffController> dff_controller, |
| thermal::DffController::Create({ |
| .id = dff_config.id(), |
| .zone_manager = zone_id_to_zone_manager[zone_id].get(), |
| .dff_loop = std::move(dff_loop), |
| .input_sensors = {dff_config.input_sensors().begin(), |
| dff_config.input_sensors().end()}, |
| .affected_controller_ids = |
| {dff_config.affected_controller_ids().begin(), |
| dff_config.affected_controller_ids().end()}, |
| })); |
| |
| LOG(WARNING) << absl::StrFormat("DFF controller %s created for zone %d", |
| dff_controller->GetId(), zone_id); |
| |
| controller_id_to_zone_ids[dff_controller->GetId()].push_back( |
| dff_controller->GetZoneManager()->GetId()); |
| zone_id_and_controller_id_to_dff_controller[std::make_tuple( |
| zone_id, dff_controller->GetId())] = std::move(dff_controller); |
| } |
| } |
| |
| // Create supportive zone managers. |
| absl::flat_hash_map<int, std::unique_ptr<thermal::ZoneManager>> |
| zone_id_to_supportive_zone_manager; |
| for (const SupportiveZoneManagerConfig& zone_config : |
| params.supportive_zone_manager_configs |
| .supportive_zone_manager_configs()) { |
| ECCLESIA_ASSIGN_OR_RETURN( |
| std::unique_ptr<thermal::ZoneManager> supportive_zone_manager, |
| thermal::ZoneManager::Create({ |
| .id = zone_config.id(), |
| .ms_per_thermal_cycle = zone_config.ms_per_thermal_cycle(), |
| .debug_mode = thermal::DebugMode( |
| zone_config.debug_mode().debug_enabled(), |
| zone_config.debug_mode().debug_pid_enabled(), |
| zone_config.debug_mode().debug_dff_enabled(), tuning_enabled), |
| .aggregator = aggregator, |
| .fru_collector = fru_collector, |
| .entity_config = entity_config, |
| .input_sensors = {zone_config.input_sensors().begin(), |
| zone_config.input_sensors().end()}, |
| .sample_data_file_prefix = sample_data_file_prefix, |
| })); |
| |
| LOG(WARNING) << absl::StrFormat("Supportive zone manager %d created", |
| supportive_zone_manager->GetId()); |
| |
| zone_id_to_supportive_zone_manager[zone_config.id()] = |
| std::move(supportive_zone_manager); |
| } |
| |
| // Create EAT controllers. |
| absl::flat_hash_map<std::tuple<int, std::string>, |
| std::unique_ptr<thermal::EatController>> |
| zone_id_and_controller_id_to_eat_controller; |
| for (const EatControllerConfig& eat_config : |
| params.eat_controller_configs.eat_controller_configs()) { |
| for (int zone_id : eat_config.zone_manager_id()) { |
| auto eat_loop = std::make_unique<thermal::EatThermalLoop>( |
| eat_config.eat_loop_config()); |
| |
| ECCLESIA_ASSIGN_OR_RETURN( |
| std::unique_ptr<thermal::EatController> eat_controller, |
| thermal::EatController::Create({ |
| .id = eat_config.id(), |
| .zone_manager = zone_id_to_supportive_zone_manager[zone_id].get(), |
| .eat_loop = std::move(eat_loop), |
| .input_sensors = {eat_config.input_sensors().begin(), |
| eat_config.input_sensors().end()}, |
| .affected_zone_ids = {eat_config.affected_zone_ids().begin(), |
| eat_config.affected_zone_ids().end()}, |
| })); |
| |
| LOG(WARNING) << absl::StrFormat("EAT controller %s created for zone %d", |
| eat_controller->GetId(), zone_id); |
| |
| controller_id_to_zone_ids[eat_controller->GetId()].push_back( |
| eat_controller->GetZoneManager()->GetId()); |
| zone_id_and_controller_id_to_eat_controller[std::make_tuple( |
| zone_id, eat_controller->GetId())] = std::move(eat_controller); |
| } |
| } |
| |
| return absl::WrapUnique(new ThermalCollector( |
| std::move(zone_id_and_controller_id_to_first_order_adrc_controller), |
| std::move(zone_id_and_controller_id_to_second_order_adrc_controller), |
| std::move(zone_id_to_zone_manager), |
| std::move(zone_id_and_controller_id_to_pid_controller), |
| std::move(zone_id_and_controller_id_to_stepwise_controller), |
| std::move(zone_id_and_controller_id_to_fan_pid_controller), |
| std::move(zone_id_and_controller_id_to_dff_controller), |
| std::move(controller_id_to_zone_ids), |
| std::move(zone_id_to_supportive_zone_manager), |
| std::move(zone_id_and_controller_id_to_eat_controller), |
| std::move(thermal_thread_manager), params.thread_factory)); |
| } |
| |
| std::vector<const thermal::ZoneManager*> ThermalCollector::GetAllThermalZones() |
| const { |
| std::vector<const thermal::ZoneManager*> thermal_zones; |
| thermal_zones.reserve(zone_id_to_zone_manager_.size() + |
| zone_id_to_supportive_zone_manager_.size()); |
| for (const auto& [zone_id, zone_manager] : zone_id_to_zone_manager_) { |
| thermal_zones.push_back(zone_manager.get()); |
| } |
| for (const auto& [zone_id, zone_manager] : |
| zone_id_to_supportive_zone_manager_) { |
| thermal_zones.push_back(zone_manager.get()); |
| } |
| std::sort(thermal_zones.begin(), thermal_zones.end(), |
| [](const thermal::ZoneManager* a, const thermal::ZoneManager* b) { |
| return a->GetId() < b->GetId(); |
| }); |
| return thermal_zones; |
| } |
| |
| std::vector<const thermal::FirstOrderAdrcController*> |
| ThermalCollector::GetAllFirstOrderAdrcControllers() const { |
| std::vector<const thermal::FirstOrderAdrcController*> |
| first_order_adrc_controllers; |
| first_order_adrc_controllers.reserve( |
| controller_id_to_controller_.controller_id_to_first_order_adrc_controller |
| .size()); |
| for (const auto& [controller_id, first_order_adrc_controller] : |
| controller_id_to_controller_ |
| .controller_id_to_first_order_adrc_controller) { |
| first_order_adrc_controllers.push_back(first_order_adrc_controller); |
| } |
| std::sort(first_order_adrc_controllers.begin(), |
| first_order_adrc_controllers.end(), |
| [](const thermal::FirstOrderAdrcController* a, |
| const thermal::FirstOrderAdrcController* b) { |
| return a->GetId() < b->GetId(); |
| }); |
| return first_order_adrc_controllers; |
| } |
| |
| std::vector<const thermal::SecondOrderAdrcController*> |
| ThermalCollector::GetAllSecondOrderAdrcControllers() const { |
| std::vector<const thermal::SecondOrderAdrcController*> |
| second_order_adrc_controllers; |
| second_order_adrc_controllers.reserve( |
| controller_id_to_controller_.controller_id_to_second_order_adrc_controller |
| .size()); |
| for (const auto& [controller_id, second_order_adrc_controller] : |
| controller_id_to_controller_ |
| .controller_id_to_second_order_adrc_controller) { |
| second_order_adrc_controllers.push_back(second_order_adrc_controller); |
| } |
| std::sort(second_order_adrc_controllers.begin(), |
| second_order_adrc_controllers.end(), |
| [](const thermal::SecondOrderAdrcController* a, |
| const thermal::SecondOrderAdrcController* b) { |
| return a->GetId() < b->GetId(); |
| }); |
| return second_order_adrc_controllers; |
| } |
| |
| std::vector<const thermal::PidController*> |
| ThermalCollector::GetAllPidControllers() const { |
| std::vector<const thermal::PidController*> pid_controllers; |
| pid_controllers.reserve( |
| controller_id_to_controller_.controller_id_to_pid_controller.size()); |
| for (const auto& [controller_id, pid_controller] : |
| controller_id_to_controller_.controller_id_to_pid_controller) { |
| pid_controllers.push_back(pid_controller); |
| } |
| std::sort( |
| pid_controllers.begin(), pid_controllers.end(), |
| [](const thermal::PidController* a, const thermal::PidController* b) { |
| return a->GetId() < b->GetId(); |
| }); |
| return pid_controllers; |
| } |
| |
| std::vector<const thermal::StepwiseController*> |
| ThermalCollector::GetAllStepwiseControllers() const { |
| std::vector<const thermal::StepwiseController*> stepwise_controllers; |
| stepwise_controllers.reserve( |
| controller_id_to_controller_.controller_id_to_stepwise_controller.size()); |
| for (const auto& [controller_id, stepwise_controller] : |
| controller_id_to_controller_.controller_id_to_stepwise_controller) { |
| stepwise_controllers.push_back(stepwise_controller); |
| } |
| std::sort(stepwise_controllers.begin(), stepwise_controllers.end(), |
| [](const thermal::StepwiseController* a, |
| const thermal::StepwiseController* b) { |
| return a->GetId() < b->GetId(); |
| }); |
| return stepwise_controllers; |
| } |
| |
| std::vector<const thermal::FanPidController*> |
| ThermalCollector::GetAllFanPidControllers() const { |
| std::vector<const thermal::FanPidController*> fan_pid_controllers; |
| fan_pid_controllers.reserve( |
| controller_id_to_controller_.controller_id_to_fan_pid_controller.size()); |
| for (const auto& [controller_id, fan_pid_controller] : |
| controller_id_to_controller_.controller_id_to_fan_pid_controller) { |
| fan_pid_controllers.push_back(fan_pid_controller); |
| } |
| std::sort(fan_pid_controllers.begin(), fan_pid_controllers.end(), |
| [](const thermal::FanPidController* a, |
| const thermal::FanPidController* b) { |
| return a->GetId() < b->GetId(); |
| }); |
| return fan_pid_controllers; |
| } |
| |
| std::vector<const thermal::DffController*> |
| ThermalCollector::GetAllDffControllers() const { |
| std::vector<const thermal::DffController*> dff_controllers; |
| dff_controllers.reserve( |
| controller_id_to_controller_.controller_id_to_dff_controller.size()); |
| for (const auto& [controller_id, dff_controller] : |
| controller_id_to_controller_.controller_id_to_dff_controller) { |
| dff_controllers.push_back(dff_controller); |
| } |
| std::sort( |
| dff_controllers.begin(), dff_controllers.end(), |
| [](const thermal::DffController* a, const thermal::DffController* b) { |
| return a->GetId() < b->GetId(); |
| }); |
| return dff_controllers; |
| } |
| |
| std::vector<const thermal::EatController*> |
| ThermalCollector::GetAllEatControllers() const { |
| std::vector<const thermal::EatController*> eat_controllers; |
| eat_controllers.reserve( |
| controller_id_to_controller_.controller_id_to_eat_controller.size()); |
| for (const auto& [controller_id, eat_controller] : |
| controller_id_to_controller_.controller_id_to_eat_controller) { |
| eat_controllers.push_back(eat_controller); |
| } |
| std::sort( |
| eat_controllers.begin(), eat_controllers.end(), |
| [](const thermal::EatController* a, const thermal::EatController* b) { |
| return a->GetId() < b->GetId(); |
| }); |
| return eat_controllers; |
| } |
| |
| absl::StatusOr<const thermal::ZoneManager*> |
| ThermalCollector::GetThermalZoneById(int zone_id) const { |
| // First check if the zone is a non-supportive zone. |
| auto itr_zone_manager = zone_id_to_zone_manager_.find(zone_id); |
| // If not, check if the zone is a supportive zone. |
| auto itr_supportive_zone_manager = |
| zone_id_to_supportive_zone_manager_.find(zone_id); |
| if (itr_zone_manager == zone_id_to_zone_manager_.end() && |
| itr_supportive_zone_manager == |
| zone_id_to_supportive_zone_manager_.end()) { |
| return absl::NotFoundError( |
| absl::StrCat("Zone with id ", zone_id, " not found")); |
| } |
| return itr_zone_manager != zone_id_to_zone_manager_.end() |
| ? itr_zone_manager->second.get() |
| : itr_supportive_zone_manager->second.get(); |
| } |
| |
| absl::StatusOr<const thermal::FirstOrderAdrcController*> |
| ThermalCollector::GetFirstOrderAdrcControllerById( |
| absl::string_view controller_id) const { |
| auto itr = |
| controller_id_to_controller_.controller_id_to_first_order_adrc_controller |
| .find(controller_id); |
| if (itr == controller_id_to_controller_ |
| .controller_id_to_first_order_adrc_controller.end()) { |
| return absl::NotFoundError(absl::StrCat("FirstOrderAdrcController with id ", |
| controller_id, " not found")); |
| } |
| return itr->second; |
| } |
| |
| absl::StatusOr<const thermal::SecondOrderAdrcController*> |
| ThermalCollector::GetSecondOrderAdrcControllerById( |
| absl::string_view controller_id) const { |
| auto itr = |
| controller_id_to_controller_.controller_id_to_second_order_adrc_controller |
| .find(controller_id); |
| if (itr == controller_id_to_controller_ |
| .controller_id_to_second_order_adrc_controller.end()) { |
| return absl::NotFoundError(absl::StrCat( |
| "SecondOrderAdrcController with id ", controller_id, " not found")); |
| } |
| return itr->second; |
| } |
| |
| absl::StatusOr<const thermal::PidController*> |
| ThermalCollector::GetPidControllerById(absl::string_view controller_id) const { |
| auto itr = controller_id_to_controller_.controller_id_to_pid_controller.find( |
| controller_id); |
| if (itr == |
| controller_id_to_controller_.controller_id_to_pid_controller.end()) { |
| return absl::NotFoundError( |
| absl::StrCat("PidController with id ", controller_id, " not found")); |
| } |
| return itr->second; |
| } |
| |
| absl::StatusOr<const thermal::StepwiseController*> |
| ThermalCollector::GetStepwiseControllerById( |
| absl::string_view controller_id) const { |
| auto itr = |
| controller_id_to_controller_.controller_id_to_stepwise_controller.find( |
| controller_id); |
| if (itr == |
| controller_id_to_controller_.controller_id_to_stepwise_controller.end()) { |
| return absl::NotFoundError(absl::StrCat("StepwiseController with id ", |
| controller_id, " not found")); |
| } |
| return itr->second; |
| } |
| |
| absl::StatusOr<const thermal::FanPidController*> |
| ThermalCollector::GetFanPidControllerById( |
| absl::string_view controller_id) const { |
| auto itr = |
| controller_id_to_controller_.controller_id_to_fan_pid_controller.find( |
| controller_id); |
| if (itr == |
| controller_id_to_controller_.controller_id_to_fan_pid_controller.end()) { |
| return absl::NotFoundError( |
| absl::StrCat("FanPidController with id ", controller_id, " not found")); |
| } |
| return itr->second; |
| } |
| |
| absl::StatusOr<const thermal::DffController*> |
| ThermalCollector::GetDffControllerById(absl::string_view controller_id) const { |
| auto itr = controller_id_to_controller_.controller_id_to_dff_controller.find( |
| controller_id); |
| if (itr == |
| controller_id_to_controller_.controller_id_to_dff_controller.end()) { |
| return absl::NotFoundError( |
| absl::StrCat("DffController with id ", controller_id, " not found")); |
| } |
| return itr->second; |
| } |
| |
| absl::StatusOr<const thermal::EatController*> |
| ThermalCollector::GetEatControllerById(absl::string_view controller_id) const { |
| auto itr = controller_id_to_controller_.controller_id_to_eat_controller.find( |
| controller_id); |
| if (itr == |
| controller_id_to_controller_.controller_id_to_eat_controller.end()) { |
| return absl::NotFoundError( |
| absl::StrCat("EatController with id ", controller_id, " not found")); |
| } |
| return itr->second; |
| } |
| |
| nlohmann::json ThermalCollector::ToJson() const { |
| nlohmann::json::object_t response; |
| |
| nlohmann::json::object_t zone_managers; |
| for (const auto& [zone_id, zone_manager] : zone_id_to_zone_manager_) { |
| nlohmann::json zone_manager_json; |
| zone_manager_json["zone_index"] = zone_id; |
| zone_manager_json["failsafe_percent"] = |
| zone_manager->GetZoneFailsafePercent(); |
| zone_manager_json["fan_mode"] = |
| (zone_manager->GetManualMode() ? "Manual" : "Auto"); |
| zone_manager_json["min_thermal_output"] = |
| zone_manager->GetMinimumThermalSetpoint(); |
| |
| zone_managers[absl::StrCat("Zone_", zone_id)] = |
| std::move(zone_manager_json); |
| } |
| response["Zones"] = std::move(zone_managers); |
| |
| nlohmann::json::object_t pid_controllers; |
| for (const auto& [controller_id, pid_controller] : |
| controller_id_to_controller_.controller_id_to_pid_controller) { |
| nlohmann::json pid_controller_json; |
| pid_controller_json["feed_forward_gain"] = |
| pid_controller->GetPidLoop()->GetFeedForwardGain(); |
| pid_controller_json["feed_forward_offset"] = |
| pid_controller->GetPidLoop()->GetFeedForwardOffset(); |
| pid_controller_json["proportional_coefficient"] = |
| pid_controller->GetPidLoop()->GetCoeffProportional(); |
| pid_controller_json["integral_coefficient"] = |
| pid_controller->GetPidLoop()->GetCoeffIntegral(); |
| pid_controller_json["derivative_coefficient"] = |
| pid_controller->GetPidLoop()->GetCoeffDerivative(); |
| pid_controller_json["integral_limit_max"] = |
| pid_controller->GetPidLoop()->GetIntegralLimitMax(); |
| pid_controller_json["integral_limit_min"] = |
| pid_controller->GetPidLoop()->GetIntegralLimitMin(); |
| pid_controller_json["output_limit_max"] = |
| pid_controller->GetPidLoop()->GetOutputLimitMax(); |
| pid_controller_json["output_limit_min"] = |
| pid_controller->GetPidLoop()->GetOutputLimitMin(); |
| pid_controller_json["slew_neg"] = |
| pid_controller->GetPidLoop()->GetSlewNeg(); |
| pid_controller_json["slew_pos"] = |
| pid_controller->GetPidLoop()->GetSlewPos(); |
| pid_controller_json["setpoint"] = pid_controller->GetSetpoint(); |
| for (const PidControllerSensorInfo& sensor : |
| pid_controller->GetInputSensors()) { |
| pid_controller_json["input_sensors"].push_back(sensor.sensor_key()); |
| } |
| if (controller_id_to_zone_ids_.contains(controller_id)) { |
| pid_controller_json["owner_zone"] = |
| controller_id_to_zone_ids_.at(controller_id); |
| } |
| |
| pid_controllers[controller_id] = std::move(pid_controller_json); |
| } |
| response["PidControllers"] = std::move(pid_controllers); |
| |
| nlohmann::json::object_t fan_pid_controllers; |
| for (const auto& [controller_id, fan_pid_controller] : |
| controller_id_to_controller_.controller_id_to_fan_pid_controller) { |
| nlohmann::json fan_pid_controller_json; |
| fan_pid_controller_json["feed_forward_gain"] = |
| fan_pid_controller->GetPidLoop()->GetFeedForwardGain(); |
| fan_pid_controller_json["feed_forward_offset"] = |
| fan_pid_controller->GetPidLoop()->GetFeedForwardOffset(); |
| fan_pid_controller_json["proportional_coefficient"] = |
| fan_pid_controller->GetPidLoop()->GetCoeffProportional(); |
| fan_pid_controller_json["integral_coefficient"] = |
| fan_pid_controller->GetPidLoop()->GetCoeffIntegral(); |
| fan_pid_controller_json["derivative_coefficient"] = |
| fan_pid_controller->GetPidLoop()->GetCoeffDerivative(); |
| fan_pid_controller_json["integral_limit_max"] = |
| fan_pid_controller->GetPidLoop()->GetIntegralLimitMax(); |
| fan_pid_controller_json["integral_limit_min"] = |
| fan_pid_controller->GetPidLoop()->GetIntegralLimitMin(); |
| fan_pid_controller_json["output_limit_max"] = |
| fan_pid_controller->GetPidLoop()->GetOutputLimitMax(); |
| fan_pid_controller_json["output_limit_min"] = |
| fan_pid_controller->GetPidLoop()->GetOutputLimitMin(); |
| fan_pid_controller_json["slew_neg"] = |
| fan_pid_controller->GetPidLoop()->GetSlewNeg(); |
| fan_pid_controller_json["slew_pos"] = |
| fan_pid_controller->GetPidLoop()->GetSlewPos(); |
| for (absl::string_view fan : fan_pid_controller->GetInputFans()) { |
| fan_pid_controller_json["input_fans"].push_back(fan); |
| } |
| for (absl::string_view fan_name : fan_pid_controller->GetOutputFans()) { |
| fan_pid_controller_json["output_fans"].push_back(fan_name); |
| } |
| if (controller_id_to_zone_ids_.contains(controller_id)) { |
| fan_pid_controller_json["owner_zone"] = |
| controller_id_to_zone_ids_.at(controller_id); |
| } |
| |
| fan_pid_controllers[controller_id] = std::move(fan_pid_controller_json); |
| } |
| response["FanPidControllers"] = std::move(fan_pid_controllers); |
| |
| nlohmann::json::object_t second_order_adrc_controllers; |
| for (const auto& [controller_id, second_order_adrc_controller] : |
| controller_id_to_controller_ |
| .controller_id_to_second_order_adrc_controller) { |
| nlohmann::json second_order_adrc_controller_json = |
| second_order_adrc_controller->ToJson(); |
| if (auto it = controller_id_to_zone_ids_.find(controller_id); |
| it != controller_id_to_zone_ids_.end()) { |
| second_order_adrc_controller_json["owner_zone"] = it->second; |
| } |
| |
| second_order_adrc_controllers[controller_id] = |
| std::move(second_order_adrc_controller_json); |
| } |
| response["SecondOrderAdrcControllers"] = |
| std::move(second_order_adrc_controllers); |
| |
| nlohmann::json::object_t first_order_adrc_controllers; |
| for (const auto& [controller_id, first_order_adrc_controller] : |
| controller_id_to_controller_ |
| .controller_id_to_first_order_adrc_controller) { |
| nlohmann::json first_order_adrc_controller_json = |
| first_order_adrc_controller->ToJson(); |
| if (auto it = controller_id_to_zone_ids_.find(controller_id); |
| it != controller_id_to_zone_ids_.end()) { |
| first_order_adrc_controller_json["owner_zone"] = it->second; |
| } |
| |
| first_order_adrc_controllers[controller_id] = |
| std::move(first_order_adrc_controller_json); |
| } |
| response["FirstOrderAdrcControllers"] = |
| std::move(first_order_adrc_controllers); |
| |
| return response; |
| } |
| |
| void ThermalCollector::StartThermalControl() { |
| for (const auto& [zone_id, zone_manager] : zone_id_to_zone_manager_) { |
| auto timer = std::make_shared<boost::asio::steady_timer>( |
| **thermal_thread_manager_->io_contexts.begin()); |
| thermal_control_timers_.push_back(timer); |
| LOG(WARNING) << "Start thermal control for zone `" << zone_id << "`"; |
| |
| boost::asio::post(**thermal_thread_manager_->io_contexts.begin(), |
| [this, zone_manager = zone_manager.get(), timer] { |
| ProcessThermalControl( |
| zone_manager, timer, /*first_run=*/true, |
| /*ms_elapsed_in_current_cycle=*/0); |
| }); |
| } |
| for (const auto& [zone_id, zone_manager] : |
| zone_id_to_supportive_zone_manager_) { |
| auto timer = std::make_shared<boost::asio::steady_timer>( |
| **thermal_thread_manager_->io_contexts.begin()); |
| thermal_control_timers_.push_back(timer); |
| LOG(WARNING) << "Start thermal control for supportive zone `" << zone_id |
| << "`"; |
| |
| boost::asio::post(**thermal_thread_manager_->io_contexts.begin(), |
| [this, zone_manager = zone_manager.get(), timer] { |
| ProcessThermalControl( |
| zone_manager, timer, /*first_run=*/true, |
| /*ms_elapsed_in_current_cycle=*/0); |
| }); |
| } |
| } |
| |
| void ThermalCollector::StartCollection() { |
| StartThermalControl(); |
| |
| for (const auto& io_context : thermal_thread_manager_->io_contexts) { |
| thermal_thread_manager_->threads.push_back( |
| thread_factory_->New([io_context]() { io_context->run(); })); |
| } |
| } |
| |
| void ThermalCollector::SetManualMode(bool enable) { |
| for (const auto& [zone_id, zone_manager] : zone_id_to_zone_manager_) { |
| zone_manager->SetManualMode(enable); |
| zone_manager->TryLogThermalDebugMessage( |
| absl::StrFormat("Zone %d set to manual mode %s.", zone_id, |
| (enable ? "true" : "false"))); |
| } |
| } |
| |
| absl::flat_hash_map<int, bool> ThermalCollector::GetZoneIdToManualModes() |
| const { |
| absl::flat_hash_map<int, bool> zone_id_to_manual_modes; |
| for (const auto& [zone_id, zone_manager] : zone_id_to_zone_manager_) { |
| zone_id_to_manual_modes[zone_manager->GetId()] = |
| zone_manager->GetManualMode(); |
| } |
| return zone_id_to_manual_modes; |
| } |
| |
| void ThermalCollector::SetManualPwm(absl::string_view fan_name, double pwm) { |
| for (const auto& [zone_id, zone_manager] : zone_id_to_zone_manager_) { |
| zone_manager->SetManualPwm(fan_name, pwm); |
| } |
| } |
| |
| absl::Status ThermalCollector::RequestPidControllerCoefficientsTuning( |
| absl::string_view controller_id, |
| const thermal::PidController::PidControllerCoefficients& coefficients) { |
| auto pid_controller_it = |
| controller_id_to_controller_.controller_id_to_pid_controller.find( |
| controller_id); |
| if (pid_controller_it == |
| controller_id_to_controller_.controller_id_to_pid_controller.end()) { |
| return absl::NotFoundError( |
| absl::StrCat("PidController with id ", controller_id, " not found")); |
| } |
| if (!pid_controller_it->second->GetZoneManager()->GetTuningEnabled()) { |
| return absl::InternalError( |
| absl::StrCat("Tuning mode is not enabled for the controller ", |
| controller_id, "'s zone.")); |
| } |
| |
| auto zone_ids_it = controller_id_to_zone_ids_.find(controller_id); |
| if (zone_ids_it == controller_id_to_zone_ids_.end()) { |
| return absl::NotFoundError(absl::StrCat( |
| "There is no zone associated with controller ", controller_id)); |
| } |
| |
| absl::MutexLock lock(tuning_mutex_); |
| controller_id_to_pending_pid_coefficient_[controller_id] = coefficients; |
| for (int zone_id : zone_ids_it->second) { |
| zone_ids_with_pending_tuning_request_.insert(zone_id); |
| } |
| return absl::OkStatus(); |
| } |
| |
| absl::Status ThermalCollector::RequestFanPidControllerCoefficientsTuning( |
| absl::string_view controller_id, |
| const thermal::FanPidController::FanPidControllerCoefficients& |
| coefficients) { |
| auto fan_pid_controller_it = |
| controller_id_to_controller_.controller_id_to_fan_pid_controller.find( |
| controller_id); |
| if (fan_pid_controller_it == |
| controller_id_to_controller_.controller_id_to_fan_pid_controller.end()) { |
| return absl::NotFoundError( |
| absl::StrCat("FanPidController with id ", controller_id, " not found")); |
| } |
| if (!fan_pid_controller_it->second->GetZoneManager()->GetTuningEnabled()) { |
| return absl::InternalError( |
| absl::StrCat("Tuning mode is not enabled for the controller ", |
| controller_id, "'s zone.")); |
| } |
| |
| auto zone_ids_it = controller_id_to_zone_ids_.find(controller_id); |
| if (zone_ids_it == controller_id_to_zone_ids_.end()) { |
| return absl::NotFoundError(absl::StrCat( |
| "There is no zone associated with controller ", controller_id)); |
| } |
| |
| absl::MutexLock lock(tuning_mutex_); |
| controller_id_to_pending_fan_pid_coefficient_[controller_id] = coefficients; |
| for (int zone_id : zone_ids_it->second) { |
| zone_ids_with_pending_tuning_request_.insert(zone_id); |
| } |
| return absl::OkStatus(); |
| } |
| |
| void ThermalCollector::ApplyPendingTuningRequests(int zone_id) { |
| absl::MutexLock lock(tuning_mutex_); |
| |
| if (!zone_ids_with_pending_tuning_request_.contains(zone_id)) { |
| return; |
| } |
| |
| thermal::ZoneManager* zone_manager = zone_id_to_zone_manager_[zone_id].get(); |
| |
| // Update coefficients for thermal controllers. |
| for (const thermal::ThermalController* controller : |
| zone_manager->GetAllThermalControllers()) { |
| const std::string controller_id = controller->GetId(); |
| |
| // Try to apply PID controller tuning requests. |
| auto pid_controller_it = zone_id_and_controller_id_to_pid_controller_.find( |
| std::make_tuple(zone_id, controller_id)); |
| if (pid_controller_it != |
| zone_id_and_controller_id_to_pid_controller_.end()) { |
| auto pending_pid_coefficient_it = |
| controller_id_to_pending_pid_coefficient_.find(controller_id); |
| if (pending_pid_coefficient_it != |
| controller_id_to_pending_pid_coefficient_.end()) { |
| pid_controller_it->second->SetCoefficients( |
| pending_pid_coefficient_it->second); |
| } |
| } |
| } |
| |
| // Update coefficients for fan controllers. |
| for (const thermal::ThermalController* controller : |
| zone_manager->GetAllFanControllers()) { |
| const std::string controller_id = controller->GetId(); |
| |
| // Try to apply fan PID controller tuning requests. |
| auto fan_pid_controller_it = |
| zone_id_and_controller_id_to_fan_pid_controller_.find( |
| std::make_tuple(zone_id, controller_id)); |
| if (fan_pid_controller_it != |
| zone_id_and_controller_id_to_fan_pid_controller_.end()) { |
| auto pending_fan_pid_coefficient_it = |
| controller_id_to_pending_fan_pid_coefficient_.find(controller_id); |
| if (pending_fan_pid_coefficient_it != |
| controller_id_to_pending_fan_pid_coefficient_.end()) { |
| fan_pid_controller_it->second->SetCoefficients( |
| pending_fan_pid_coefficient_it->second); |
| } |
| } |
| } |
| |
| zone_ids_with_pending_tuning_request_.erase(zone_id); |
| } |
| |
| bool ThermalCollector::HasPendingTuningRequest( |
| absl::string_view controller_id) { |
| auto zone_ids_it = controller_id_to_zone_ids_.find(controller_id); |
| if (zone_ids_it == controller_id_to_zone_ids_.end()) { |
| return false; |
| } |
| |
| absl::MutexLock lock(tuning_mutex_); |
| for (int zone_id : zone_ids_it->second) { |
| if (zone_ids_with_pending_tuning_request_.contains(zone_id)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| absl::StatusOr<thermal::PidOptimizerInfo> ThermalCollector::CreatePidAutoTuner( |
| const PidControllerConfig& pid_controller_config) { |
| // Create the auto-tuner. |
| std::unique_ptr<thermal::optimizer::PidOptimizer> pid_optimizer = nullptr; |
| if (pid_controller_config.pid_autotuner_config() |
| .has_ziegler_nichols_config()) { |
| ECCLESIA_ASSIGN_OR_RETURN( |
| pid_optimizer, thermal::optimizer::PidAutotunerZieglerNichols::Create( |
| pid_controller_config)); |
| } else if (pid_controller_config.pid_autotuner_config() |
| .has_differentiated_ziegler_nichols_config()) { |
| ECCLESIA_ASSIGN_OR_RETURN( |
| pid_optimizer, |
| thermal::optimizer::PidAutotunerDifferentiatedZieglerNichols::Create( |
| pid_controller_config)); |
| } else if (pid_controller_config.pid_autotuner_config() |
| .has_frequency_response_estimation_config()) { |
| ECCLESIA_ASSIGN_OR_RETURN( |
| pid_optimizer, |
| thermal::optimizer::PidAutotunerFrequencyResponseEstimation::Create( |
| pid_controller_config)); |
| } |
| |
| if (pid_optimizer == nullptr) { |
| return absl::InvalidArgumentError( |
| "No valid PID auto-tuner config is found in the PID controller " |
| "config."); |
| } |
| |
| // Set up auto-tuner states. |
| thermal::optimizer::PidOptimizerStates pid_optimizer_states; |
| pid_optimizer_states.pid_optimizer_init_params = { |
| .coeff_p = pid_controller_config.pid_loop_config().coeff_proportional(), |
| .coeff_i = pid_controller_config.pid_loop_config().coeff_integral(), |
| .coeff_d = pid_controller_config.pid_loop_config().coeff_derivative(), |
| }; |
| |
| return thermal::PidOptimizerInfo{ |
| .states = pid_optimizer_states, |
| .optimizer = std::move(pid_optimizer), |
| }; |
| } |
| |
| void ThermalCollector::ProcessOneThermalLoop( |
| thermal::ZoneManager* zone_manager) { |
| zone_manager->ClearSetpoints(); |
| zone_manager->UpdateAllSensorValues(); |
| zone_manager->ProcessThermalControllers(); |
| zone_manager->DetermineMaximumSetpoint(); |
| } |
| |
| void ThermalCollector::ProcessThermalControl( |
| thermal::ZoneManager* zone_manager, |
| const std::shared_ptr<boost::asio::steady_timer>& timer, bool first_run, |
| uint64_t ms_elapsed_in_current_cycle) { |
| if (stop_thermal_control_) { |
| return; |
| } |
| |
| std::chrono::steady_clock::time_point next_activation_time; |
| if (first_run) { |
| zone_manager->InitializeThermalStates(); |
| ProcessOneThermalLoop(zone_manager); |
| next_activation_time = std::chrono::steady_clock::now(); |
| // Avoid dumping the first iteration data, which is for preparation of |
| // successive runs. |
| if (zone_manager->GetDebugEnabled()) { |
| zone_manager->ClearSampledData(); |
| } |
| } else { |
| next_activation_time = timer->expiry(); |
| } |
| |
| uint64_t ms_per_fan_cycle = zone_manager->GetMsPerFanCycle(); |
| // Push forward the original expiration time of timer, instead of just |
| // resetting it with expires_after() from now, to make sure the interval |
| // is of the expected duration, and not stretched out by CPU time taken. |
| next_activation_time += std::chrono::milliseconds(ms_per_fan_cycle); |
| timer->expires_at(next_activation_time); |
| |
| timer->async_wait( |
| [this, zone_manager, timer, ms_elapsed_in_current_cycle, |
| ms_per_fan_cycle](const boost::system::error_code& ec) mutable { |
| if (stop_thermal_control_.load(std::memory_order_relaxed) || |
| ec == boost::asio::error::operation_aborted || |
| ec == boost::system::errc::operation_canceled) { |
| return; // timer being canceled, stop loop |
| } |
| |
| // Under tuning mode, try update coefficients first, if there is any |
| // pending request. |
| if (zone_manager->GetTuningEnabled()) { |
| ApplyPendingTuningRequests(zone_manager->GetId()); |
| } |
| |
| // Under manual mode, simply trigger the next thermal control iteration, |
| // and then return, without executing the thermal and fan cycles. |
| if (zone_manager->GetManualMode()) { |
| ProcessThermalControl(zone_manager, timer, /*first_run=*/false, |
| ms_elapsed_in_current_cycle); |
| return; |
| } |
| |
| uint64_t ms_per_thermal_cycle = zone_manager->GetMsPerThermalCycle(); |
| |
| // Process thermal cycles no more frequently than fan cycles. |
| // If the thermal interval is not an exact multiple of the fan interval, |
| // there will be some remainder left over. |
| bool process_thermal_cycle = false; |
| if (ms_elapsed_in_current_cycle >= ms_per_thermal_cycle) { |
| // Keep the timing correct, as the intervals are overlapping. |
| ms_elapsed_in_current_cycle -= ms_per_thermal_cycle; |
| ProcessOneThermalLoop(zone_manager); |
| process_thermal_cycle = true; |
| } else { |
| // We need to update fan sensor values for fan controllers in EVERY |
| // iteration. Alternatively, we may just update fan rpms here, instead |
| // of all sensors, but this is hardly a performance improvement, and |
| // will introduce more complexity. |
| zone_manager->UpdateAllSensorValues(); |
| } |
| |
| // Process fan controllers and write fan speeds in every iteration. |
| zone_manager->ProcessFanControllers(); |
| zone_manager->WriteFanSpeeds(); |
| |
| if (zone_manager->GetDebugEnabled()) { |
| // Clear the sampled data if only fan cycles are processed, to avoid |
| // data misalignment. |
| process_thermal_cycle ? zone_manager->DumpSampledData() |
| : zone_manager->ClearSampledData(); |
| } |
| |
| // Count how many milliseconds have elapsed to determine when to perform |
| // thermal cycles, in a proper ratio with fan cycles. |
| ms_elapsed_in_current_cycle += ms_per_fan_cycle; |
| |
| ProcessThermalControl(zone_manager, timer, |
| /*first_run=*/false, ms_elapsed_in_current_cycle); |
| }); |
| } |
| |
| std::unique_ptr<EmptyThermalCollector> EmptyThermalCollector::Create() { |
| return std::make_unique<EmptyThermalCollector>(); |
| } |
| |
| std::vector<const thermal::ZoneManager*> |
| EmptyThermalCollector::GetAllThermalZones() const { |
| LOG(WARNING) << "EmptyThermalCollector::GetAllThermalZones is " |
| "called. This will return an empty list."; |
| return {}; |
| } |
| |
| std::vector<const thermal::FirstOrderAdrcController*> |
| EmptyThermalCollector::GetAllFirstOrderAdrcControllers() const { |
| LOG(WARNING) << "EmptyThermalCollector::GetAllFirstOrderAdrcControllers is " |
| "called. This will return an empty list."; |
| return {}; |
| } |
| |
| std::vector<const thermal::SecondOrderAdrcController*> |
| EmptyThermalCollector::GetAllSecondOrderAdrcControllers() const { |
| LOG(WARNING) << "EmptyThermalCollector::GetAllSecondOrderAdrcControllers is " |
| "called. This will return an empty list."; |
| return {}; |
| } |
| |
| std::vector<const thermal::PidController*> |
| EmptyThermalCollector::GetAllPidControllers() const { |
| LOG(WARNING) << "EmptyThermalCollector::GetAllPidControllers is " |
| "called. This will return an empty list."; |
| return {}; |
| } |
| |
| std::vector<const thermal::StepwiseController*> |
| EmptyThermalCollector::GetAllStepwiseControllers() const { |
| LOG(WARNING) << "EmptyThermalCollector::GetAllStepwiseControllers is " |
| "called. This will return an empty list."; |
| return {}; |
| } |
| |
| std::vector<const thermal::DffController*> |
| EmptyThermalCollector::GetAllDffControllers() const { |
| LOG(WARNING) << "EmptyThermalCollector::GetAllDffControllers is " |
| "called. This will return an empty list."; |
| return {}; |
| } |
| |
| std::vector<const thermal::FanPidController*> |
| EmptyThermalCollector::GetAllFanPidControllers() const { |
| LOG(WARNING) << "EmptyThermalCollector::GetAllFanPidControllers is " |
| "called. This will return an empty list."; |
| return {}; |
| } |
| |
| std::vector<const thermal::EatController*> |
| EmptyThermalCollector::GetAllEatControllers() const { |
| LOG(WARNING) << "EmptyThermalCollector::GetAllEatControllers is " |
| "called. This will return an empty list."; |
| return {}; |
| } |
| |
| absl::StatusOr<const thermal::ZoneManager*> |
| EmptyThermalCollector::GetThermalZoneById(int zone_id) const { |
| LOG(WARNING) << "EmptyThermalCollector::GetThermalZoneById is " |
| "called. This will return a nullptr."; |
| return nullptr; |
| } |
| |
| absl::StatusOr<const thermal::FirstOrderAdrcController*> |
| EmptyThermalCollector::GetFirstOrderAdrcControllerById( |
| absl::string_view controller_id) const { |
| LOG(WARNING) << "EmptyThermalCollector::GetFirstOrderAdrcControllerById is " |
| "called. This will return a nullptr."; |
| return nullptr; |
| } |
| |
| absl::StatusOr<const thermal::SecondOrderAdrcController*> |
| EmptyThermalCollector::GetSecondOrderAdrcControllerById( |
| absl::string_view controller_id) const { |
| LOG(WARNING) << "EmptyThermalCollector::GetSecondOrderAdrcControllerById is " |
| "called. This will return a nullptr."; |
| return nullptr; |
| } |
| |
| absl::StatusOr<const thermal::PidController*> |
| EmptyThermalCollector::GetPidControllerById( |
| absl::string_view controller_id) const { |
| LOG(WARNING) << "EmptyThermalCollector::GetPidControllerById is " |
| "called. This will return a nullptr."; |
| return nullptr; |
| } |
| |
| absl::StatusOr<const thermal::StepwiseController*> |
| EmptyThermalCollector::GetStepwiseControllerById( |
| absl::string_view controller_id) const { |
| LOG(WARNING) << "EmptyThermalCollector::GetStepwiseControllerById is " |
| "called. This will return a nullptr."; |
| return nullptr; |
| } |
| |
| absl::StatusOr<const thermal::DffController*> |
| EmptyThermalCollector::GetDffControllerById( |
| absl::string_view controller_id) const { |
| LOG(WARNING) << "EmptyThermalCollector::GetDffControllerById is " |
| "called. This will return a nullptr."; |
| return nullptr; |
| } |
| |
| absl::StatusOr<const thermal::EatController*> |
| EmptyThermalCollector::GetEatControllerById( |
| absl::string_view controller_id) const { |
| LOG(WARNING) << "EmptyThermalCollector::GetEatControllerById is " |
| "called. This will return a nullptr."; |
| return nullptr; |
| } |
| |
| absl::StatusOr<const thermal::FanPidController*> |
| EmptyThermalCollector::GetFanPidControllerById( |
| absl::string_view controller_id) const { |
| LOG(WARNING) << "EmptyThermalCollector::GetFanPidControllerById is " |
| "called. This will return a nullptr."; |
| return nullptr; |
| } |
| |
| nlohmann::json EmptyThermalCollector::GetSchedulerStats() const { |
| return nlohmann::json::parse( |
| "{\"Warning\": \"EmptyThermalCollector used.\"}"); |
| } |
| |
| nlohmann::json EmptyThermalCollector::ToJson() const { |
| return nlohmann::json::parse( |
| "{\"Warning\": \"EmptyThermalCollector used.\"}"); |
| } |
| |
| void EmptyThermalCollector::StartCollection() { |
| LOG(WARNING) << "EmptyThermalCollector::StartCollection is " |
| "called. This does nothing."; |
| } |
| |
| void EmptyThermalCollector::SetManualMode(bool enable) { |
| LOG(WARNING) << "EmptyThermalCollector::SetManualMode is " |
| "called. This does nothing."; |
| } |
| |
| absl::flat_hash_map<int, bool> EmptyThermalCollector::GetZoneIdToManualModes() |
| const { |
| LOG(WARNING) << "EmptyThermalCollector::GetZoneIdToManualModes is " |
| "called. This will return an empty map."; |
| return {}; |
| } |
| |
| void EmptyThermalCollector::SetManualPwm(absl::string_view fan_name, |
| double pwm) { |
| LOG(WARNING) << "EmptyThermalCollector::SetManualPwm is " |
| "called. This does nothing."; |
| } |
| |
| absl::Status EmptyThermalCollector::RequestPidControllerCoefficientsTuning( |
| absl::string_view controller_id, |
| const thermal::PidController::PidControllerCoefficients& coefficients) { |
| LOG(WARNING) |
| << "EmptyThermalCollector::RequestPidControllerCoefficientsTuning is " |
| "called. This does nothing."; |
| return absl::OkStatus(); |
| } |
| |
| absl::Status EmptyThermalCollector::RequestFanPidControllerCoefficientsTuning( |
| absl::string_view controller_id, |
| const thermal::FanPidController::FanPidControllerCoefficients& |
| coefficients) { |
| LOG(WARNING) |
| << "EmptyThermalCollector::RequestFanPidControllerCoefficientsTuning is " |
| "called. This does nothing."; |
| return absl::OkStatus(); |
| } |
| |
| } // namespace milotic_tlbmc |