blob: 8619d28ff498d56a3be48fa6ad28e21a5eec86bc [file] [log] [blame]
#include "rvc-device.h"
#include <string>
using namespace chip::app::Clusters;
void RvcDevice::Init()
{
mServiceAreaInstance.Init();
mRunModeInstance.Init();
mCleanModeInstance.Init();
mOperationalStateInstance.Init();
}
void RvcDevice::SetDeviceToIdleState()
{
if (mCharging)
{
mDocked = true;
mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kCharging));
}
else if (mDocked)
{
mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kDocked));
}
else
{
mOperationalStateInstance.SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kStopped));
}
}
void RvcDevice::HandleRvcRunChangeToMode(uint8_t newMode, ModeBase::Commands::ChangeToModeResponse::Type & response)
{
uint8_t currentState = mOperationalStateInstance.GetCurrentOperationalState();
uint8_t currentMode = mRunModeInstance.GetCurrentMode();
switch (currentState)
{
case to_underlying(OperationalState::OperationalStateEnum::kStopped):
case to_underlying(RvcOperationalState::OperationalStateEnum::kDocked):
case to_underlying(RvcOperationalState::OperationalStateEnum::kCharging): {
// We could be in the charging state with an RvcRun mode != idle.
if (currentMode != RvcRunMode::ModeIdle && newMode != RvcRunMode::ModeIdle)
{
response.status = to_underlying(ModeBase::StatusCode::kInvalidInMode);
response.statusText.SetValue(
chip::CharSpan::fromCharString("Change to the mapping or cleaning mode is only allowed from idle"));
return;
}
mCharging = false;
mDocked = false;
mRunModeInstance.UpdateCurrentMode(newMode);
mOperationalStateInstance.SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kRunning));
mServiceAreaDelegate.SetAttributesAtCleanStart();
response.status = to_underlying(ModeBase::StatusCode::kSuccess);
return;
}
break;
case to_underlying(OperationalState::OperationalStateEnum::kRunning): {
if (newMode != RvcRunMode::ModeIdle)
{
response.status = to_underlying(ModeBase::StatusCode::kInvalidInMode);
response.statusText.SetValue(
chip::CharSpan::fromCharString("Change to the mapping or cleaning mode is only allowed from idle"));
return;
}
mRunModeInstance.UpdateCurrentMode(newMode);
mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kSeekingCharger));
response.status = to_underlying(ModeBase::StatusCode::kSuccess);
UpdateServiceAreaProgressOnExit();
return;
}
break;
}
// If we fall through at any point, it's because the change is not supported in the current state.
response.status = to_underlying(ModeBase::StatusCode::kInvalidInMode);
response.statusText.SetValue(chip::CharSpan::fromCharString("This change is not allowed at this time"));
}
void RvcDevice::HandleRvcCleanChangeToMode(uint8_t newMode, ModeBase::Commands::ChangeToModeResponse::Type & response)
{
uint8_t rvcRunCurrentMode = mRunModeInstance.GetCurrentMode();
if (rvcRunCurrentMode != RvcRunMode::ModeIdle)
{
response.status = to_underlying(ModeBase::StatusCode::kInvalidInMode);
response.statusText.SetValue(chip::CharSpan::fromCharString("Change of the cleaning mode is only allowed in Idle."));
return;
}
response.status = to_underlying(ModeBase::StatusCode::kSuccess);
}
void RvcDevice::HandleOpStatePauseCallback(Clusters::OperationalState::GenericOperationalError & err)
{
// This method is only called if the device is in a Pause-compatible state, i.e. `Running` or `SeekingCharger`.
mStateBeforePause = mOperationalStateInstance.GetCurrentOperationalState();
auto error = mOperationalStateInstance.SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kPaused));
err.Set((error == CHIP_NO_ERROR) ? to_underlying(OperationalState::ErrorStateEnum::kNoError)
: to_underlying(OperationalState::ErrorStateEnum::kUnableToCompleteOperation));
}
void RvcDevice::HandleOpStateResumeCallback(Clusters::OperationalState::GenericOperationalError & err)
{
uint8_t targetState = to_underlying(OperationalState::OperationalStateEnum::kRunning);
switch (mOperationalStateInstance.GetCurrentOperationalState())
{
case to_underlying(RvcOperationalState::OperationalStateEnum::kCharging):
case to_underlying(RvcOperationalState::OperationalStateEnum::kDocked): {
if (mRunModeInstance.GetCurrentMode() != RvcRunMode::ModeCleaning &&
mRunModeInstance.GetCurrentMode() != RvcRunMode::ModeMapping)
{
err.Set(to_underlying(OperationalState::ErrorStateEnum::kCommandInvalidInState));
return;
}
}
break;
case to_underlying(OperationalState::OperationalStateEnum::kPaused): {
if (mStateBeforePause == to_underlying(RvcOperationalState::OperationalStateEnum::kSeekingCharger))
{
targetState = to_underlying(RvcOperationalState::OperationalStateEnum::kSeekingCharger);
}
}
break;
default:
// This method is only called if the device is in a resume-compatible state, i.e. `Charging`, `Docked` or
// `Paused`. Therefore, we do not expect to ever enter this branch.
err.Set(to_underlying(OperationalState::ErrorStateEnum::kCommandInvalidInState));
return;
}
auto error = mOperationalStateInstance.SetOperationalState(targetState);
err.Set((error == CHIP_NO_ERROR) ? to_underlying(OperationalState::ErrorStateEnum::kNoError)
: to_underlying(OperationalState::ErrorStateEnum::kUnableToCompleteOperation));
}
void RvcDevice::HandleOpStateGoHomeCallback(Clusters::OperationalState::GenericOperationalError & err)
{
switch (mOperationalStateInstance.GetCurrentOperationalState())
{
case to_underlying(OperationalState::OperationalStateEnum::kStopped): {
if (mRunModeInstance.GetCurrentMode() != RvcRunMode::ModeIdle)
{
err.Set(to_underlying(OperationalState::ErrorStateEnum::kCommandInvalidInState));
return;
}
auto error = mOperationalStateInstance.SetOperationalState(
to_underlying(RvcOperationalState::OperationalStateEnum::kSeekingCharger));
err.Set((error == CHIP_NO_ERROR) ? to_underlying(OperationalState::ErrorStateEnum::kNoError)
: to_underlying(OperationalState::ErrorStateEnum::kUnableToCompleteOperation));
}
break;
default:
err.Set(to_underlying(OperationalState::ErrorStateEnum::kCommandInvalidInState));
return;
}
}
bool RvcDevice::SaIsSetSelectedAreasAllowed(MutableCharSpan & statusText)
{
if (mOperationalStateInstance.GetCurrentOperationalState() == to_underlying(OperationalState::OperationalStateEnum::kRunning))
{
CopyCharSpanToMutableCharSpan("cannot set the Selected Areas while the device is running"_span, statusText);
return false;
}
return true;
}
bool RvcDevice::SaHandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan & skipStatusText)
{
if (mServiceAreaInstance.GetCurrentArea() != skippedArea)
{
// This device only supports skipping the current location.
CopyCharSpanToMutableCharSpan("the skipped area does not match the current area"_span, skipStatusText);
return false;
}
if (mOperationalStateInstance.GetCurrentOperationalState() != to_underlying(OperationalState::OperationalStateEnum::kRunning))
{
// This device only accepts the skip are command while in the running state
CopyCharSpanToMutableCharSpan("skip area is only accepted when the device is running"_span, skipStatusText);
return false;
}
bool finished;
mServiceAreaDelegate.GoToNextArea(ServiceArea::OperationalStatusEnum::kSkipped, finished);
if (finished)
{
HandleActivityCompleteEvent();
}
return true;
}
bool RvcDevice::SaIsSupportedAreasChangeAllowed()
{
return mOperationalStateInstance.GetCurrentOperationalState() !=
to_underlying(OperationalState::OperationalStateEnum::kRunning);
}
bool RvcDevice::SaIsSupportedMapChangeAllowed()
{
return mOperationalStateInstance.GetCurrentOperationalState() !=
to_underlying(OperationalState::OperationalStateEnum::kRunning);
}
void RvcDevice::HandleChargedMessage()
{
if (mOperationalStateInstance.GetCurrentOperationalState() !=
to_underlying(RvcOperationalState::OperationalStateEnum::kCharging))
{
ChipLogError(NotSpecified, "RVC App: The 'Charged' command is only accepted when the device is in the 'Charging' state.");
return;
}
mCharging = false;
if (mRunModeInstance.GetCurrentMode() == RvcRunMode::ModeIdle)
{
if (mDocked) // assuming that we can't be charging the device while it is not docked.
{
mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kDocked));
}
else
{
mOperationalStateInstance.SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kStopped));
}
}
else
{
mOperationalStateInstance.SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kRunning));
}
}
void RvcDevice::HandleChargingMessage()
{
if (mOperationalStateInstance.GetCurrentOperationalState() != to_underlying(RvcOperationalState::OperationalStateEnum::kDocked))
{
ChipLogError(NotSpecified, "RVC App: The 'Charging' command is only accepted when the device is in the 'Docked' state.");
return;
}
mCharging = true;
mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kCharging));
}
void RvcDevice::HandleDockedMessage()
{
if (mOperationalStateInstance.GetCurrentOperationalState() != to_underlying(OperationalState::OperationalStateEnum::kStopped))
{
ChipLogError(NotSpecified, "RVC App: The 'Docked' command is only accepted when the device is in the 'Stopped' state.");
return;
}
mDocked = true;
mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kDocked));
}
void RvcDevice::HandleChargerFoundMessage()
{
if (mOperationalStateInstance.GetCurrentOperationalState() !=
to_underlying(RvcOperationalState::OperationalStateEnum::kSeekingCharger))
{
ChipLogError(NotSpecified,
"RVC App: The 'ChargerFound' command is only accepted when the device is in the 'SeekingCharger' state.");
return;
}
mCharging = true;
mDocked = true;
mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kCharging));
}
void RvcDevice::HandleLowChargeMessage()
{
if (mOperationalStateInstance.GetCurrentOperationalState() != to_underlying(OperationalState::OperationalStateEnum::kRunning))
{
ChipLogError(NotSpecified, "RVC App: The 'LowCharge' command is only accepted when the device is in the 'Running' state.");
return;
}
mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kSeekingCharger));
}
void RvcDevice::HandleActivityCompleteEvent()
{
if (mOperationalStateInstance.GetCurrentOperationalState() != to_underlying(OperationalState::OperationalStateEnum::kRunning))
{
ChipLogError(NotSpecified,
"RVC App: The 'ActivityComplete' command is only accepted when the device is in the 'Running' state.");
return;
}
mRunModeInstance.UpdateCurrentMode(RvcRunMode::ModeIdle);
Optional<DataModel::Nullable<uint32_t>> a(DataModel::Nullable<uint32_t>(100));
Optional<DataModel::Nullable<uint32_t>> b(DataModel::Nullable<uint32_t>(10));
mOperationalStateInstance.OnOperationCompletionDetected(0, a, b);
mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kSeekingCharger));
mServiceAreaInstance.SetCurrentArea(DataModel::NullNullable);
mServiceAreaInstance.SetEstimatedEndTime(DataModel::NullNullable);
UpdateServiceAreaProgressOnExit();
}
void RvcDevice::HandleAreaCompletedEvent()
{
bool finished;
mServiceAreaDelegate.GoToNextArea(ServiceArea::OperationalStatusEnum::kCompleted, finished);
if (finished)
{
HandleActivityCompleteEvent();
}
}
void RvcDevice::HandleErrorEvent(const std::string & error)
{
detail::Structs::ErrorStateStruct::Type err;
if (error == "UnableToStartOrResume")
{
err.errorStateID = to_underlying(OperationalState::ErrorStateEnum::kUnableToStartOrResume);
}
else if (error == "UnableToCompleteOperation")
{
err.errorStateID = to_underlying(OperationalState::ErrorStateEnum::kUnableToCompleteOperation);
}
else if (error == "CommandInvalidInState")
{
err.errorStateID = to_underlying(OperationalState::ErrorStateEnum::kCommandInvalidInState);
}
else if (error == "FailedToFindChargingDock")
{
err.errorStateID = to_underlying(RvcOperationalState::ErrorStateEnum::kFailedToFindChargingDock);
}
else if (error == "Stuck")
{
err.errorStateID = to_underlying(RvcOperationalState::ErrorStateEnum::kStuck);
}
else if (error == "DustBinMissing")
{
err.errorStateID = to_underlying(RvcOperationalState::ErrorStateEnum::kDustBinMissing);
}
else if (error == "DustBinFull")
{
err.errorStateID = to_underlying(RvcOperationalState::ErrorStateEnum::kDustBinFull);
}
else if (error == "WaterTankEmpty")
{
err.errorStateID = to_underlying(RvcOperationalState::ErrorStateEnum::kWaterTankEmpty);
}
else if (error == "WaterTankMissing")
{
err.errorStateID = to_underlying(RvcOperationalState::ErrorStateEnum::kWaterTankMissing);
}
else if (error == "WaterTankLidOpen")
{
err.errorStateID = to_underlying(RvcOperationalState::ErrorStateEnum::kWaterTankLidOpen);
}
else if (error == "MopCleaningPadMissing")
{
err.errorStateID = to_underlying(RvcOperationalState::ErrorStateEnum::kMopCleaningPadMissing);
}
else
{
ChipLogError(NotSpecified, "Unhandled command: The 'Error' key of the 'ErrorEvent' message is not valid.");
return;
}
mOperationalStateInstance.OnOperationalErrorDetected(err);
}
void RvcDevice::HandleClearErrorMessage()
{
if (mOperationalStateInstance.GetCurrentOperationalState() != to_underlying(OperationalState::OperationalStateEnum::kError))
{
ChipLogError(NotSpecified, "RVC App: The 'ClearError' command is only excepted when the device is in the 'Error' state.");
return;
}
mRunModeInstance.UpdateCurrentMode(RvcRunMode::ModeIdle);
SetDeviceToIdleState();
}
void RvcDevice::HandleResetMessage()
{
mRunModeInstance.UpdateCurrentMode(RvcRunMode::ModeIdle);
mOperationalStateInstance.SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kStopped));
mCleanModeInstance.UpdateCurrentMode(RvcCleanMode::ModeQuick);
mServiceAreaInstance.ClearSelectedAreas();
mServiceAreaInstance.ClearProgress();
mServiceAreaInstance.SetCurrentArea(DataModel::NullNullable);
mServiceAreaInstance.SetEstimatedEndTime(DataModel::NullNullable);
}
void RvcDevice::UpdateServiceAreaProgressOnExit()
{
if (!mServiceAreaInstance.HasFeature(ServiceArea::Feature::kProgressReporting))
{
return;
}
uint32_t i = 0;
ServiceArea::Structs::ProgressStruct::Type progressElement;
while (mServiceAreaDelegate.GetProgressElementByIndex(i, progressElement))
{
if (progressElement.status == ServiceArea::OperationalStatusEnum::kOperating ||
progressElement.status == ServiceArea::OperationalStatusEnum::kPending)
{
mServiceAreaInstance.SetProgressStatus(progressElement.areaID, ServiceArea::OperationalStatusEnum::kSkipped);
}
i++;
}
}