blob: 14c07818aa8bcd3ac69d1a1fb4c6a22418f68351 [file] [log] [blame]
// 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 <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/gyroscope_calibration_utils_impl.h"
namespace rmad {
RunCalibrationStateHandler::RunCalibrationStateHandler(
scoped_refptr<JsonStore> json_store)
: BaseStateHandler(json_store) {
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) {
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);
}
running_group_ = CalibrationSetupInstruction_MAX;
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.
RetrieveVarsAndCalibrate();
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 {.error = RMAD_ERROR_REQUEST_INVALID, .state_case = GetStateCase()};
}
// 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, but it may fail or is still in progress (hanging?). This is why we
// should check here.
RmadErrorCode error_code;
if (ShouldRecalibrate(&error_code)) {
if (error_code != RMAD_ERROR_OK) {
LOG(ERROR) << "Rmad: The sensor calibration is failed.";
return {.error = error_code,
.state_case = RmadState::StateCase::kCheckCalibration};
} else {
LOG(INFO) << "Rmad: The sensor calibration needs another round.";
return {.error = error_code,
.state_case = RmadState::StateCase::kSetupCalibration};
}
}
state_ = state;
return {.error = RMAD_ERROR_OK,
.state_case = RmadState::StateCase::kProvisionDevice};
}
void 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;
}
if (!GetCurrentSetupInstruction(calibration_map_, &running_group_)) {
calibration_overall_signal_sender_->Run(
RMAD_CALIBRATION_OVERALL_INITIALIZATION_FAILED);
LOG(ERROR) << "Failed to get components to be calibrated.";
return;
}
if (running_group_ == RMAD_CALIBRATION_INSTRUCTION_NO_NEED_CALIBRATION) {
calibration_overall_signal_sender_->Run(RMAD_CALIBRATION_OVERALL_COMPLETE);
return;
}
for (auto component_status : calibration_map_[running_group_]) {
if (ShouldCalibrate(component_status.second)) {
CalibrateAndSendProgress(component_status.first);
}
}
}
bool RunCalibrationStateHandler::ShouldRecalibrate(RmadErrorCode* error_code) {
*error_code = RMAD_ERROR_OK;
for (auto instruction_components : calibration_map_) {
CalibrationSetupInstruction setup_instruction =
instruction_components.first;
for (auto component_status : instruction_components.second) {
if (component_status.first == RMAD_COMPONENT_UNKNOWN) {
*error_code = RMAD_ERROR_CALIBRATION_COMPONENT_MISSING;
return true;
}
if (component_status.second ==
CalibrationComponentStatus::RMAD_CALIBRATION_UNKNOWN) {
*error_code = RMAD_ERROR_CALIBRATION_STATUS_MISSING;
return true;
}
if (!IsValidCalibrationComponent(component_status.first)) {
*error_code = RMAD_ERROR_CALIBRATION_COMPONENT_INVALID;
return true;
}
if (ShouldCalibrate(component_status.second)) {
// Under normal situations, we expect that the calibration has been
// completed here, but it may fail, stuck or signal loss. Therefore, we
// expect Chrome to still send the request after the timeout. This is
// why we allow different statuses here.
if (setup_instruction == running_group_) {
*error_code = RMAD_ERROR_CALIBRATION_FAILED;
}
return true;
}
}
}
return false;
}
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;
}
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_[running_group_][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_[running_group_][component] = status;
SetCalibrationMap(json_store_, calibration_map_);
bool is_in_progress = false;
bool is_failed = false;
for (auto component_status_map : calibration_map_[running_group_]) {
is_in_progress |=
component_status_map.second ==
CalibrationComponentStatus::RMAD_CALIBRATION_IN_PROGRESS;
is_failed |= component_status_map.second ==
CalibrationComponentStatus::RMAD_CALIBRATION_FAILED;
}
// We only update the overall status after all calibrations are done.
if (!is_in_progress) {
is_failed |= (vpd_utils_thread_safe_.get() &&
!vpd_utils_thread_safe_->FlushOutRoVpdCache());
if (is_failed) {
calibration_overall_signal_sender_->Run(
CalibrationOverallStatus::
RMAD_CALIBRATION_OVERALL_CURRENT_ROUND_FAILED);
} else if (RmadErrorCode ignore; ShouldRecalibrate(&ignore)) {
calibration_overall_signal_sender_->Run(
CalibrationOverallStatus::
RMAD_CALIBRATION_OVERALL_CURRENT_ROUND_COMPLETE);
} else {
calibration_overall_signal_sender_->Run(
CalibrationOverallStatus::RMAD_CALIBRATION_OVERALL_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