| // Copyright 2021 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "rmad/state_handler/run_calibration_state_handler.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| |
| #include <base/logging.h> |
| #include <base/task/task_traits.h> |
| #include <base/task/thread_pool.h> |
| |
| #include "rmad/utils/accelerometer_calibration_utils_impl.h" |
| #include "rmad/utils/calibration_utils.h" |
| #include "rmad/utils/fake_sensor_calibration_utils.h" |
| #include "rmad/utils/gyroscope_calibration_utils_impl.h" |
| |
| namespace rmad { |
| |
| namespace fake { |
| |
| FakeRunCalibrationStateHandler::FakeRunCalibrationStateHandler( |
| scoped_refptr<JsonStore> json_store) |
| : RunCalibrationStateHandler( |
| json_store, |
| std::make_unique<FakeSensorCalibrationUtils>(), |
| std::make_unique<FakeSensorCalibrationUtils>(), |
| std::make_unique<FakeSensorCalibrationUtils>(), |
| std::make_unique<FakeSensorCalibrationUtils>()) {} |
| |
| } // namespace fake |
| |
| RunCalibrationStateHandler::RunCalibrationStateHandler( |
| scoped_refptr<JsonStore> json_store) |
| : BaseStateHandler(json_store), |
| calibration_overall_signal_sender_(base::DoNothing()), |
| calibration_component_signal_sender_(base::DoNothing()) { |
| vpd_utils_thread_safe_ = base::MakeRefCounted<VpdUtilsImplThreadSafe>(); |
| sensor_calibration_utils_map_[RMAD_COMPONENT_BASE_ACCELEROMETER] = |
| std::make_unique<AccelerometerCalibrationUtilsImpl>( |
| vpd_utils_thread_safe_, "base"); |
| sensor_calibration_utils_map_[RMAD_COMPONENT_LID_ACCELEROMETER] = |
| std::make_unique<AccelerometerCalibrationUtilsImpl>( |
| vpd_utils_thread_safe_, "lid"); |
| sensor_calibration_utils_map_[RMAD_COMPONENT_BASE_GYROSCOPE] = |
| std::make_unique<GyroscopeCalibrationUtilsImpl>(vpd_utils_thread_safe_, |
| "base"); |
| sensor_calibration_utils_map_[RMAD_COMPONENT_LID_GYROSCOPE] = |
| std::make_unique<GyroscopeCalibrationUtilsImpl>(vpd_utils_thread_safe_, |
| "lid"); |
| } |
| |
| RunCalibrationStateHandler::RunCalibrationStateHandler( |
| scoped_refptr<JsonStore> json_store, |
| std::unique_ptr<SensorCalibrationUtils> base_acc_utils, |
| std::unique_ptr<SensorCalibrationUtils> lid_acc_utils, |
| std::unique_ptr<SensorCalibrationUtils> base_gyro_utils, |
| std::unique_ptr<SensorCalibrationUtils> lid_gyro_utils) |
| : BaseStateHandler(json_store), |
| calibration_overall_signal_sender_(base::DoNothing()), |
| calibration_component_signal_sender_(base::DoNothing()) { |
| sensor_calibration_utils_map_[RMAD_COMPONENT_BASE_ACCELEROMETER] = |
| std::move(base_acc_utils); |
| sensor_calibration_utils_map_[RMAD_COMPONENT_LID_ACCELEROMETER] = |
| std::move(lid_acc_utils); |
| sensor_calibration_utils_map_[RMAD_COMPONENT_BASE_GYROSCOPE] = |
| std::move(base_gyro_utils); |
| sensor_calibration_utils_map_[RMAD_COMPONENT_LID_GYROSCOPE] = |
| std::move(lid_gyro_utils); |
| } |
| |
| RmadErrorCode RunCalibrationStateHandler::InitializeState() { |
| if (!state_.has_run_calibration()) { |
| state_.set_allocated_run_calibration(new RunCalibrationState); |
| } |
| setup_instruction_ = RMAD_CALIBRATION_INSTRUCTION_NEED_TO_CHECK; |
| if (std::string calibration_instruction; |
| !json_store_->GetValue(kCalibrationInstruction, |
| &calibration_instruction) || |
| !CalibrationSetupInstruction_Parse(calibration_instruction, |
| &setup_instruction_)) { |
| LOG(WARNING) << "Device hasn't been setup for calibration yet!"; |
| } |
| |
| if (!task_runner_) { |
| task_runner_ = base::ThreadPool::CreateTaskRunner( |
| {base::TaskPriority::BEST_EFFORT, base::MayBlock()}); |
| } |
| progress_timer_map_[RMAD_COMPONENT_BASE_ACCELEROMETER] = |
| std::make_unique<base::RepeatingTimer>(); |
| progress_timer_map_[RMAD_COMPONENT_LID_ACCELEROMETER] = |
| std::make_unique<base::RepeatingTimer>(); |
| progress_timer_map_[RMAD_COMPONENT_BASE_GYROSCOPE] = |
| std::make_unique<base::RepeatingTimer>(); |
| progress_timer_map_[RMAD_COMPONENT_LID_GYROSCOPE] = |
| std::make_unique<base::RepeatingTimer>(); |
| |
| // We will run the calibration in RetrieveVarsAndCalibrate. |
| if (!RetrieveVarsAndCalibrate()) { |
| return RMAD_ERROR_STATE_HANDLER_INITIALIZATION_FAILED; |
| } |
| return RMAD_ERROR_OK; |
| } |
| |
| void RunCalibrationStateHandler::CleanUpState() { |
| for (auto& progress_timer : progress_timer_map_) { |
| if (!progress_timer.second) { |
| continue; |
| } |
| if (progress_timer.second->IsRunning()) { |
| progress_timer.second->Stop(); |
| } |
| progress_timer.second.reset(); |
| } |
| progress_timer_map_.clear(); |
| if (task_runner_) { |
| task_runner_.reset(); |
| } |
| if (vpd_utils_thread_safe_.get()) { |
| vpd_utils_thread_safe_->FlushOutRoVpdCache(); |
| } |
| } |
| |
| BaseStateHandler::GetNextStateCaseReply |
| RunCalibrationStateHandler::GetNextStateCase(const RmadState& state) { |
| if (!state.has_run_calibration()) { |
| LOG(ERROR) << "RmadState missing |run calibration| state."; |
| return NextStateCaseWrapper(RMAD_ERROR_REQUEST_INVALID); |
| } |
| |
| // kWipeDevice should be set by previous states. |
| bool wipe_device; |
| if (!json_store_->GetValue(kWipeDevice, &wipe_device)) { |
| LOG(ERROR) << "Variable " << kWipeDevice << " not found"; |
| return NextStateCaseWrapper(RMAD_ERROR_TRANSITION_FAILED); |
| } |
| |
| // Since the actual calibration has already started in InitializeState, |
| // Chrome should wait for the signal to trigger GetNextStateCaseReply. Under |
| // normal circumstances, we expect that the calibration has been completed |
| // here. Therefore, the running instruction (which will be updated during |
| // InitializeState) should be set to the next calibration instruction. |
| if (running_instruction_ == RMAD_CALIBRATION_INSTRUCTION_NEED_TO_CHECK) { |
| LOG(ERROR) << "Rmad: Sensor calibration failed."; |
| return NextStateCaseWrapper(RmadState::StateCase::kCheckCalibration); |
| } else if (running_instruction_ == |
| RMAD_CALIBRATION_INSTRUCTION_NO_NEED_CALIBRATION) { |
| if (wipe_device) { |
| return NextStateCaseWrapper(RmadState::StateCase::kFinalize); |
| } else { |
| return NextStateCaseWrapper(RmadState::StateCase::kWpEnablePhysical); |
| } |
| } else if (running_instruction_ == setup_instruction_) { |
| LOG(INFO) << "Rmad: Sensor calibrations is still running."; |
| return NextStateCaseWrapper(RMAD_ERROR_WAIT); |
| } else { |
| LOG(INFO) << "Rmad: Sensor calibration needs another round."; |
| LOG(INFO) << CalibrationSetupInstruction_Name(running_instruction_); |
| return NextStateCaseWrapper(RmadState::StateCase::kSetupCalibration); |
| } |
| } |
| |
| BaseStateHandler::GetNextStateCaseReply |
| RunCalibrationStateHandler::TryGetNextStateCaseAtBoot() { |
| // We don't expect any reboot during calibration, and here is the part right |
| // after rebooting. Therefore, we will mark any unexpected status to failed |
| // and transition to kCheckCalibration for further error handling. |
| for (auto [instruction, components] : calibration_map_) { |
| for (auto [component, status] : components) { |
| if (IsInProgressStatus(status) || IsUnknownStatus(status)) { |
| calibration_map_[instruction][component] = |
| CalibrationComponentStatus::RMAD_CALIBRATION_FAILED; |
| } |
| } |
| } |
| |
| // Since we want to keep all error handling in kCheckCalibration , it is |
| // only logged here if writing to the status file fails. |
| if (!SetCalibrationMap(json_store_, calibration_map_)) { |
| LOG(ERROR) << "Failed to set calibration variables"; |
| } |
| |
| return NextStateCaseWrapper(RmadState::StateCase::kCheckCalibration); |
| } |
| |
| bool RunCalibrationStateHandler::RetrieveVarsAndCalibrate() { |
| if (!GetCalibrationMap(json_store_, &calibration_map_)) { |
| calibration_overall_signal_sender_.Run( |
| RMAD_CALIBRATION_OVERALL_INITIALIZATION_FAILED); |
| LOG(ERROR) << "Failed to read calibration variables"; |
| return false; |
| } |
| |
| running_instruction_ = GetCurrentSetupInstruction(calibration_map_); |
| if (running_instruction_ == RMAD_CALIBRATION_INSTRUCTION_NEED_TO_CHECK) { |
| calibration_overall_signal_sender_.Run( |
| RMAD_CALIBRATION_OVERALL_INITIALIZATION_FAILED); |
| return true; |
| } |
| |
| if (running_instruction_ == |
| RMAD_CALIBRATION_INSTRUCTION_NO_NEED_CALIBRATION) { |
| calibration_overall_signal_sender_.Run(RMAD_CALIBRATION_OVERALL_COMPLETE); |
| return true; |
| } |
| |
| if (setup_instruction_ == running_instruction_) { |
| for (auto [component, status] : calibration_map_[running_instruction_]) { |
| if (status == CalibrationComponentStatus::RMAD_CALIBRATION_WAITING) { |
| CalibrateAndSendProgress(component); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| void RunCalibrationStateHandler::CalibrateAndSendProgress( |
| RmadComponent component) { |
| auto& utils = sensor_calibration_utils_map_[component]; |
| if (!utils.get()) { |
| LOG(ERROR) << RmadComponent_Name(component) |
| << " does not support calibration."; |
| return; |
| } |
| |
| // We should set the state to in-progress here to handle the issue of |
| // reinitialization before the first polling task (set status and send |
| // progress). |
| calibration_map_[setup_instruction_][component] = |
| CalibrationComponentStatus::RMAD_CALIBRATION_IN_PROGRESS; |
| SetCalibrationMap(json_store_, calibration_map_); |
| |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(base::IgnoreResult(&SensorCalibrationUtils::Calibrate), |
| base::Unretained(utils.get()))); |
| LOG(INFO) << "Start calibrating for " << RmadComponent_Name(component); |
| |
| progress_timer_map_[component]->Start( |
| FROM_HERE, kPollInterval, |
| base::BindRepeating(&RunCalibrationStateHandler::CheckCalibrationTask, |
| this, component)); |
| LOG(INFO) << "Start polling calibration progress for " |
| << RmadComponent_Name(component); |
| } |
| |
| void RunCalibrationStateHandler::CheckCalibrationTask(RmadComponent component) { |
| auto& utils = sensor_calibration_utils_map_[component]; |
| double progress = 0; |
| |
| if (!utils->GetProgress(&progress)) { |
| LOG(WARNING) << "Failed to get calibration progress for " |
| << utils->GetLocation() << ":" << utils->GetName(); |
| return; |
| } |
| |
| SaveAndSend(component, progress); |
| } |
| |
| void RunCalibrationStateHandler::SaveAndSend(RmadComponent component, |
| double progress) { |
| CalibrationComponentStatus::CalibrationStatus status = |
| CalibrationComponentStatus::RMAD_CALIBRATION_IN_PROGRESS; |
| |
| if (progress >= 1.0) { |
| status = CalibrationComponentStatus::RMAD_CALIBRATION_COMPLETE; |
| } else if (progress < 0) { |
| status = CalibrationComponentStatus::RMAD_CALIBRATION_FAILED; |
| } |
| |
| auto pre_status = calibration_map_[setup_instruction_][component]; |
| if (pre_status != status) { |
| // This is a critical section, but we don't need to lock it. |
| // Instead of using a mutex to lock the critical section, we use a timer |
| // (tasks run sequentially on the main thread) to prevent race conditions. |
| calibration_map_[setup_instruction_][component] = status; |
| SetCalibrationMap(json_store_, calibration_map_); |
| |
| bool in_progress = false; |
| bool failed = false; |
| for (auto [ignore, other_status] : calibration_map_[setup_instruction_]) { |
| in_progress |= IsInProgressStatus(other_status) || |
| IsWaitingForCalibration(other_status); |
| failed |= |
| other_status == CalibrationComponentStatus::RMAD_CALIBRATION_FAILED; |
| } |
| |
| // We only update the overall status after all calibrations are done. |
| if (!in_progress) { |
| failed |= (vpd_utils_thread_safe_.get() && |
| !vpd_utils_thread_safe_->FlushOutRoVpdCache()); |
| if (failed) { |
| calibration_overall_signal_sender_.Run( |
| CalibrationOverallStatus:: |
| RMAD_CALIBRATION_OVERALL_CURRENT_ROUND_FAILED); |
| } else if (GetCurrentSetupInstruction(calibration_map_) == |
| RMAD_CALIBRATION_INSTRUCTION_NO_NEED_CALIBRATION) { |
| calibration_overall_signal_sender_.Run( |
| CalibrationOverallStatus::RMAD_CALIBRATION_OVERALL_COMPLETE); |
| } else { |
| calibration_overall_signal_sender_.Run( |
| CalibrationOverallStatus:: |
| RMAD_CALIBRATION_OVERALL_CURRENT_ROUND_COMPLETE); |
| } |
| } |
| } |
| |
| CalibrationComponentStatus component_status; |
| component_status.set_component(component); |
| component_status.set_status(status); |
| component_status.set_progress(progress); |
| calibration_component_signal_sender_.Run(std::move(component_status)); |
| |
| if (status != CalibrationComponentStatus::RMAD_CALIBRATION_IN_PROGRESS) { |
| progress_timer_map_[component]->Stop(); |
| } |
| } |
| |
| } // namespace rmad |