blob: eaae281dd6bb5d1143c91581f4d7908b32db658a [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 "rmad/utils/calibration_utils.h"
namespace rmad {
RunCalibrationStateHandler::RunCalibrationStateHandler(
scoped_refptr<JsonStore> json_store)
: BaseStateHandler(json_store) {}
RmadErrorCode RunCalibrationStateHandler::InitializeState() {
if (!state_.has_run_calibration()) {
state_.set_allocated_run_calibration(new RunCalibrationState);
}
running_group_ = CalibrationSetupInstruction_MAX;
// We will run the calibration in RetrieveVarsAndCalibrate.
RetrieveVarsAndCalibrate();
return RMAD_ERROR_OK;
}
void RunCalibrationStateHandler::CleanUpState() {
for (auto& component_timer : timer_map_) {
if (!component_timer.second) {
continue;
}
if (component_timer.second->IsRunning()) {
component_timer.second->Stop();
}
component_timer.second.reset();
}
timer_map_.clear();
}
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.";
} else {
LOG(INFO) << "Rmad: The sensor calibration needs another round.";
}
return {.error = error_code,
.state_case = RmadState::StateCase::kCheckCalibration};
}
// There's nothing in |RunCalibrations|.
state_ = state;
// TODO(genechang): We should check whether we should perform the next round
// of calibration (different setup) here.
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)) {
// TODO(genechang): Should execute calibration here.
PollUntilCalibrationDone(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 == RmadComponent::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::PollUntilCalibrationDone(
RmadComponent component) {
if (!timer_map_[component]) {
timer_map_[component] = std::make_unique<base::RepeatingTimer>();
}
if (timer_map_[component]->IsRunning()) {
timer_map_[component]->Stop();
}
if (component == RmadComponent::RMAD_COMPONENT_GYROSCOPE) {
timer_map_[component]->Start(
FROM_HERE, kPollInterval, this,
&RunCalibrationStateHandler::CheckGyroCalibrationTask);
} else if (component == RmadComponent::RMAD_COMPONENT_BASE_ACCELEROMETER) {
timer_map_[component]->Start(
FROM_HERE, kPollInterval, this,
&RunCalibrationStateHandler::CheckBaseAccCalibrationTask);
} else if (component == RmadComponent::RMAD_COMPONENT_LID_ACCELEROMETER) {
timer_map_[component]->Start(
FROM_HERE, kPollInterval, this,
&RunCalibrationStateHandler::CheckLidAccCalibrationTask);
} else {
LOG(ERROR) << RmadComponent_Name(component) << " cannot be calibrated";
return;
}
LOG(INFO) << "Start polling calibration progress for "
<< RmadComponent_Name(component);
}
void RunCalibrationStateHandler::CheckGyroCalibrationTask() {
double progress = 0;
if (!GetGyroCalibrationProgress(&progress)) {
LOG(WARNING) << "Failed to get gyroscpoe calibration progress";
return;
}
SaveAndSend(RmadComponent::RMAD_COMPONENT_GYROSCOPE, progress);
}
void RunCalibrationStateHandler::CheckBaseAccCalibrationTask() {
double progress = 0;
if (!GetBaseAccCalibrationProgress(&progress)) {
LOG(WARNING) << "Failed to get base accelerometer calibration progress";
return;
}
SaveAndSend(RmadComponent::RMAD_COMPONENT_BASE_ACCELEROMETER, progress);
}
void RunCalibrationStateHandler::CheckLidAccCalibrationTask() {
double progress = 0;
if (!GetLidAccCalibrationProgress(&progress)) {
LOG(WARNING) << "Failed to get lid accelerometer calibration progress";
return;
}
SaveAndSend(RmadComponent::RMAD_COMPONENT_LID_ACCELEROMETER, 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;
if (timer_map_[component]) {
timer_map_[component]->Stop();
}
} else if (progress < 0) {
status = CalibrationComponentStatus::RMAD_CALIBRATION_FAILED;
if (timer_map_[component]) {
timer_map_[component]->Stop();
}
}
if (calibration_map_[running_group_][component] != status) {
base::AutoLock lock_scope(calibration_mutex_);
calibration_map_[running_group_][component] = status;
SetCalibrationMap(json_store_, calibration_map_);
}
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));
}
// TODO(genechang): This is currently fake. Should check gyroscope calibration
// progress.
bool RunCalibrationStateHandler::GetGyroCalibrationProgress(double* progress) {
static double gyro_progress = 0;
*progress = gyro_progress;
gyro_progress += 0.1;
if (gyro_progress > 1.0) {
gyro_progress = 0;
}
return true;
}
// TODO(genechang): This is currently fake. Should check base acceleromoter
// calibration progress.
bool RunCalibrationStateHandler::GetBaseAccCalibrationProgress(
double* progress) {
static double base_acc_progress = 0;
*progress = base_acc_progress;
base_acc_progress += 0.1;
if (base_acc_progress > 1.0) {
base_acc_progress = 0;
}
return true;
}
// TODO(genechang): This is currently fake. Should check lid acceleromoter
// calibration progress.
bool RunCalibrationStateHandler::GetLidAccCalibrationProgress(
double* progress) {
static double lid_acc_progress = 0;
*progress = lid_acc_progress;
lid_acc_progress += 0.1;
if (lid_acc_progress > 1.0) {
lid_acc_progress = 0;
}
return true;
}
} // namespace rmad