diff --git a/examples/rvc-app/README.md b/examples/rvc-app/README.md index 2943f54103f828..7431c663efe4f9 100644 --- a/examples/rvc-app/README.md +++ b/examples/rvc-app/README.md @@ -20,6 +20,11 @@ must have a `"Name"` key that contains the command name. This name is shown in the state machine diagram above. Example `echo '{"Name": "Charged"}' > /tmp/chip_rvc_fifo_42`. +### `AreaComplete` message + +This indicates that the area currently being serviced as indicated by the +service area cluster is now complete. + ### `ErrorEvent` message The error event message requires the additional key `"Error"` which specifies @@ -37,10 +42,14 @@ and setting up the testing environment, python tests can be executed with `./scripts/tests/run_python_test.py --script src/python_testing/.py --script-args "--storage-path admin_storage.json --PICS examples/rvc-app/rvc-common/pics/RVC_App_Test_Plan.txt --int-arg "` **Note:** If the testing environment has not been commissioned with the RVC app, -use chip-tool to switch on the commissioning window -`chip-tool pairing open-commissioning-window`, and add the following flags to -the `--script-args` above. -`--commissioning-method on-network --discriminator XXXX --passcode XXXX`. + +1. use chip-tool to switch on the commissioning window + `out/debug/chip-tool pairing open-commissioning-window 0x1230 1 180 1000 42` +2. Get the manual pairing code. This will look something like + `Manual pairing code: [01073112097]`. +3. Run any one of the tests with the `--commission-only` and `--manual-code` + flags: + `./scripts/tests/run_python_test.py --script src/python_testing/TC_RVCCLEANM_1_2.py --script-args "--commissioning-method on-network --manual-code 01073112097 --commission-only"` Below are the PIXIT definitions required for the different python tests. diff --git a/examples/rvc-app/linux/RvcAppCommandDelegate.cpp b/examples/rvc-app/linux/RvcAppCommandDelegate.cpp index 58610e1b11d112..5a08dc6c9ffc8b 100644 --- a/examples/rvc-app/linux/RvcAppCommandDelegate.cpp +++ b/examples/rvc-app/linux/RvcAppCommandDelegate.cpp @@ -83,6 +83,10 @@ void RvcAppCommandHandler::HandleCommand(intptr_t context) { self->OnActivityCompleteHandler(); } + else if (name == "AreaComplete") + { + self->OnAreaCompleteHandler(); + } else if (name == "ErrorEvent") { std::string error = self->mJsonValue["Error"].asString(); @@ -140,6 +144,11 @@ void RvcAppCommandHandler::OnActivityCompleteHandler() mRvcDevice->HandleActivityCompleteEvent(); } +void RvcAppCommandHandler::OnAreaCompleteHandler() +{ + mRvcDevice->HandleAreaCompletedEvent(); +} + void RvcAppCommandHandler::OnErrorEventHandler(const std::string & error) { mRvcDevice->HandleErrorEvent(error); diff --git a/examples/rvc-app/linux/RvcAppCommandDelegate.h b/examples/rvc-app/linux/RvcAppCommandDelegate.h index 366772bf0842ab..e8dc404de2eb1c 100644 --- a/examples/rvc-app/linux/RvcAppCommandDelegate.h +++ b/examples/rvc-app/linux/RvcAppCommandDelegate.h @@ -55,6 +55,8 @@ class RvcAppCommandHandler void OnActivityCompleteHandler(); + void OnAreaCompleteHandler(); + void OnErrorEventHandler(const std::string & error); void OnClearErrorHandler(); diff --git a/examples/rvc-app/rvc-common/include/rvc-device.h b/examples/rvc-app/rvc-common/include/rvc-device.h index da03422ffbb34a..97f3475117f27a 100644 --- a/examples/rvc-app/rvc-common/include/rvc-device.h +++ b/examples/rvc-app/rvc-common/include/rvc-device.h @@ -59,6 +59,11 @@ class RvcDevice mOperationalStateDelegate.SetPauseCallback(&RvcDevice::HandleOpStatePauseCallback, this); mOperationalStateDelegate.SetResumeCallback(&RvcDevice::HandleOpStateResumeCallback, this); mOperationalStateDelegate.SetGoHomeCallback(&RvcDevice::HandleOpStateGoHomeCallback, this); + + mServiceAreaDelegate.SetIsSetSelectedAreasAllowedCallback(&RvcDevice::SaIsSetSelectedAreasAllowed, this); + mServiceAreaDelegate.SetHandleSkipCurrentAreaCallback(&RvcDevice::SaHandleSkipCurrentArea, this); + mServiceAreaDelegate.SetIsSupportedAreasChangeAllowedCallback(&RvcDevice::SaIsSupportedAreasChangeAllowed, this); + mServiceAreaDelegate.SetIsSupportedMapChangeAllowedCallback(&RvcDevice::SaIsSupportedMapChangeAllowed, this); } /** @@ -97,6 +102,14 @@ class RvcDevice */ void HandleOpStateGoHomeCallback(Clusters::OperationalState::GenericOperationalError & err); + bool SaIsSetSelectedAreasAllowed(MutableCharSpan & statusText); + + bool SaHandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan & skipStatusText); + + bool SaIsSupportedAreasChangeAllowed(); + + bool SaIsSupportedMapChangeAllowed(); + /** * Updates the state machine when the device becomes fully-charged. */ @@ -112,6 +125,8 @@ class RvcDevice void HandleActivityCompleteEvent(); + void HandleAreaCompletedEvent(); + /** * Sets the device to an error state with the error state ID matching the error name given. * @param error The error name. Could be one of UnableToStartOrResume, UnableToCompleteOperation, CommandInvalidInState, diff --git a/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h b/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h index 5397e3096c14ae..4d065782dca113 100644 --- a/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h +++ b/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h @@ -29,6 +29,10 @@ namespace Clusters { class RvcDevice; +typedef bool (RvcDevice::*IsSetSelectedAreasAllowedCallback)(MutableCharSpan & statusText); +typedef bool (RvcDevice::*HandleSkipCurrentAreaCallback)(uint32_t skippedArea, MutableCharSpan & skipStatusText); +typedef bool (RvcDevice::*IsChangeAllowedSimpleCallback)(); + namespace ServiceArea { class RvcServiceAreaDelegate : public Delegate @@ -40,16 +44,45 @@ class RvcServiceAreaDelegate : public Delegate std::vector mSelectedAreas; std::vector mProgressList; + RvcDevice * mIsSetSelectedAreasAllowedDeviceInstance; + IsSetSelectedAreasAllowedCallback mIsSetSelectedAreasAllowedCallback; + RvcDevice * mHandleSkipCurrentAreaDeviceInstance; + HandleSkipCurrentAreaCallback mHandleSkipCurrentAreaCallback; + RvcDevice * mIsSupportedAreasChangeAllowedDeviceInstance; + IsChangeAllowedSimpleCallback mIsSupportedAreasChangeAllowedCallback; + RvcDevice * mIsSupportedMapChangeAllowedDeviceInstance; + IsChangeAllowedSimpleCallback mIsSupportedMapChangeAllowedCallback; + + // hardcoded values for SUPPORTED MAPS. + const uint32_t supportedMapId_XX = 3; + const uint32_t supportedMapId_YY = 245; + + // hardcoded values for SUPPORTED AREAS. + const uint32_t supportedAreaID_A = 7; + const uint32_t supportedAreaID_B = 1234567; + const uint32_t supportedAreaID_C = 10050; + const uint32_t supportedAreaID_D = 0x88888888; + + /** + * Set the SupportedMaps and SupportedAreas where the SupportedMaps is not null. + */ + void SetMapTopology(); + + /** + * Set the SupportedMaps and SupportedAreas where the SupportedMaps is null. + */ + void SetNoMapTopology(); + public: CHIP_ERROR Init() override; // command support - bool IsSetSelectedAreasAllowed(MutableCharSpan statusText) override; + bool IsSetSelectedAreasAllowed(MutableCharSpan & statusText) override; bool IsValidSelectAreasSet(const ServiceArea::Commands::SelectAreas::DecodableType & req, - ServiceArea::SelectAreasStatus & areaStatus, MutableCharSpan statusText) override; + ServiceArea::SelectAreasStatus & areaStatus, MutableCharSpan & statusText) override; - bool HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan skipStatusText) override; + bool HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan & skipStatusText) override; //************************************************************************* // Supported Areas accessors @@ -112,6 +145,50 @@ class RvcServiceAreaDelegate : public Delegate const ServiceArea::Structs::ProgressStruct::Type & modifiedProgressElement) override; bool ClearProgress() override; + + //************************************************************************* + // RVC device callback setters + + void SetIsSetSelectedAreasAllowedCallback(IsSetSelectedAreasAllowedCallback callback, RvcDevice * instance) + { + mIsSetSelectedAreasAllowedCallback = callback; + mIsSetSelectedAreasAllowedDeviceInstance = instance; + } + + void SetHandleSkipCurrentAreaCallback(HandleSkipCurrentAreaCallback callback, RvcDevice * instance) + { + mHandleSkipCurrentAreaCallback = callback; + mHandleSkipCurrentAreaDeviceInstance = instance; + } + + void SetIsSupportedAreasChangeAllowedCallback(IsChangeAllowedSimpleCallback callback, RvcDevice * instance) + { + mIsSupportedAreasChangeAllowedCallback = callback; + mIsSupportedAreasChangeAllowedDeviceInstance = instance; + } + + void SetIsSupportedMapChangeAllowedCallback(IsChangeAllowedSimpleCallback callback, RvcDevice * instance) + { + mIsSupportedMapChangeAllowedCallback = callback; + mIsSupportedMapChangeAllowedDeviceInstance = instance; + } + + //************************************************************************* + // Helper methods for setting service area attributes. + + /** + * Sets the service area attributes at the start of a clean. + * This includes the current area an progress attributes. + */ + void SetAttributesAtCleanStart(); + + /** + * Go to the next area in the list of selected areas. + * @param currentAreaOpState The operational state to be set in the Status field of the Progress attribute for the current area. + * This can only be Completed or Skipped. + * @param finished true if there are no more areas to clean an we should end the clean. + */ + void GoToNextArea(OperationalStatusEnum currentAreaOpState, bool & finished); }; } // namespace ServiceArea diff --git a/examples/rvc-app/rvc-common/pics/rvc-app-pics-values b/examples/rvc-app/rvc-common/pics/rvc-app-pics-values index 669abd26c33f94..b158de538ed547 100644 --- a/examples/rvc-app/rvc-common/pics/rvc-app-pics-values +++ b/examples/rvc-app/rvc-common/pics/rvc-app-pics-values @@ -50,3 +50,31 @@ RVCRUNM.S.C01.Tx=1 RVCRUNM.S.F00=0 RVCRUNM.S.M.CAN_TEST_MODE_FAILURE=1 RVCRUNM.S.M.CAN_MANUALLY_CONTROLLED=1 + +SEAR.S=1 +SEAR.S.F00=0 +SEAR.S.F01=1 +SEAR.S.F02=1 +SEAR.S.A0000=1 +SEAR.S.A0001=1 +SEAR.S.A0002=1 +SEAR.S.A0003=1 +SEAR.S.A0004=1 +SEAR.S.A0005=1 +SEAR.S.C00.Rsp=1 +SEAR.S.C02.Rsp=1 +SEAR.S.C01.Tx=1 +SEAR.S.C03.Tx=1 +SEAR.S.M.REMOVE_AREA=0 +SEAR.S.M.ADD_AREA=0 +SEAR.S.M.REMOVE_MAP=0 +SEAR.S.M.ADD_MAP=0 +SEAR.S.M.INVALID_STATE_FOR_SELECT_AREAS=1 +SEAR.S.M.VALID_STATE_FOR_SELECT_AREAS=1 +SEAR.S.M.SELECT_AREAS_WHILE_NON_IDLE=1 +SEAR.S.M.HAS_MANUAL_SELAREA_STATE_CONTROL=1 +SEAR.S.M.HAS_MANUAL_SKIP_STATE_CONTROL=1 +SEAR.S.M.INVALID_STATE_FOR_SKIP=1 +SEAR.S.M.NO_SELAREA_FOR_SKIP=1 +SEAR.S.M.VALID_STATE_FOR_SKIP=1 +SEAR.S.M.HAS_MANUAL_OPERATING_STATE_CONTROL=1 \ No newline at end of file diff --git a/examples/rvc-app/rvc-common/src/rvc-device.cpp b/examples/rvc-app/rvc-common/src/rvc-device.cpp index e018e0929301c7..db938a58e07982 100644 --- a/examples/rvc-app/rvc-common/src/rvc-device.cpp +++ b/examples/rvc-app/rvc-common/src/rvc-device.cpp @@ -52,6 +52,7 @@ void RvcDevice::HandleRvcRunChangeToMode(uint8_t newMode, ModeBase::Commands::Ch mDocked = false; mRunModeInstance.UpdateCurrentMode(newMode); mOperationalStateInstance.SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kRunning)); + mServiceAreaDelegate.SetAttributesAtCleanStart(); response.status = to_underlying(ModeBase::StatusCode::kSuccess); return; } @@ -161,6 +162,55 @@ void RvcDevice::HandleOpStateGoHomeCallback(Clusters::OperationalState::GenericO } } +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() != @@ -258,6 +308,20 @@ void RvcDevice::HandleActivityCompleteEvent() mOperationalStateInstance.OnOperationCompletionDetected(0, a, b); mOperationalStateInstance.SetOperationalState(to_underlying(RvcOperationalState::OperationalStateEnum::kSeekingCharger)); + + mServiceAreaInstance.SetCurrentArea(DataModel::NullNullable); + mServiceAreaInstance.SetEstimatedEndTime(DataModel::NullNullable); +} + +void RvcDevice::HandleAreaCompletedEvent() +{ + bool finished; + mServiceAreaDelegate.GoToNextArea(ServiceArea::OperationalStatusEnum::kCompleted, finished); + + if (finished) + { + HandleActivityCompleteEvent(); + } } void RvcDevice::HandleErrorEvent(const std::string & error) @@ -334,4 +398,9 @@ 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); } diff --git a/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp b/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp index 1bc60baa0774cd..11daac10ece55c 100644 --- a/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp +++ b/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp @@ -23,21 +23,13 @@ using namespace chip; using namespace chip::app::Clusters; using namespace chip::app::Clusters::ServiceArea; -CHIP_ERROR RvcServiceAreaDelegate::Init() +void RvcServiceAreaDelegate::SetMapTopology() { - // hardcoded fill of SUPPORTED MAPS for prototyping - uint32_t supportedMapId_XX = 3; - uint32_t supportedMapId_YY = 245; + ClearSupportedMaps(); GetInstance()->AddSupportedMap(supportedMapId_XX, "My Map XX"_span); GetInstance()->AddSupportedMap(supportedMapId_YY, "My Map YY"_span); - // hardcoded fill of SUPPORTED AREAS for prototyping - uint32_t supportedAreaID_A = 7; - uint32_t supportedAreaID_B = 1234567; - uint32_t supportedAreaID_C = 10050; - uint32_t supportedAreaID_D = 0x88888888; - // Area A has name, floor number, uses map XX auto areaA = AreaStructureWrapper{} @@ -47,7 +39,7 @@ CHIP_ERROR RvcServiceAreaDelegate::Init() // Area B has name, uses map XX auto areaB = AreaStructureWrapper{} - .SetMapId(supportedAreaID_B) + .SetAreaId(supportedAreaID_B) .SetMapId(supportedMapId_XX) .SetLocationInfo("My Location B"_span, DataModel::NullNullable, DataModel::NullNullable); @@ -69,6 +61,45 @@ CHIP_ERROR RvcServiceAreaDelegate::Init() GetInstance()->AddSupportedArea(areaB); GetInstance()->AddSupportedArea(areaC); GetInstance()->AddSupportedArea(areaD); +} + +void RvcServiceAreaDelegate::SetNoMapTopology() +{ + ClearSupportedMaps(); + + // Area A has name, floor number. + auto areaA = + AreaStructureWrapper{} + .SetAreaId(supportedAreaID_A) + .SetLocationInfo("My Location A"_span, DataModel::Nullable(4), DataModel::Nullable()); + + // Area B has name. + auto areaB = AreaStructureWrapper{} + .SetAreaId(supportedAreaID_B) + .SetLocationInfo("My Location B"_span, DataModel::NullNullable, DataModel::NullNullable); + + // Area C has full SemData, no name. + auto areaC = AreaStructureWrapper{} + .SetAreaId(supportedAreaID_C) + .SetLocationInfo(""_span, -1, Globals::AreaTypeTag::kPlayRoom) + .SetLandmarkInfo(Globals::LandmarkTag::kBackDoor, Globals::RelativePositionTag::kNextTo); + + // Area D has null values for all landmark fields. + auto areaD = AreaStructureWrapper{} + .SetAreaId(supportedAreaID_D) + .SetLocationInfo("My Location D"_span, DataModel::NullNullable, DataModel::NullNullable) + .SetLandmarkInfo(Globals::LandmarkTag::kCouch, Globals::RelativePositionTag::kNextTo); + + GetInstance()->AddSupportedArea(areaA); + GetInstance()->AddSupportedArea(areaB); + GetInstance()->AddSupportedArea(areaC); + GetInstance()->AddSupportedArea(areaD); +} + +CHIP_ERROR RvcServiceAreaDelegate::Init() +{ + SetMapTopology(); + GetInstance()->SetCurrentArea(supportedAreaID_C); return CHIP_NO_ERROR; @@ -77,23 +108,81 @@ CHIP_ERROR RvcServiceAreaDelegate::Init() //************************************************************************* // command support -bool RvcServiceAreaDelegate::IsSetSelectedAreasAllowed(MutableCharSpan statusText) +bool RvcServiceAreaDelegate::IsSetSelectedAreasAllowed(MutableCharSpan & statusText) { - // TODO IMPLEMENT - return true; + return (mIsSetSelectedAreasAllowedDeviceInstance->*mIsSetSelectedAreasAllowedCallback)(statusText); }; bool RvcServiceAreaDelegate::IsValidSelectAreasSet(const Commands::SelectAreas::DecodableType & req, SelectAreasStatus & areaStatus, - MutableCharSpan statusText) + MutableCharSpan & statusText) { - // TODO IMPLEMENT + // if req is empty list return true. + { + size_t reqSize; + if (req.newAreas.ComputeSize(&reqSize) != CHIP_NO_ERROR) + { + areaStatus = SelectAreasStatus::kInvalidSet; // todo Not sure this is the correct error to use here + CopyCharSpanToMutableCharSpan("error computing number of selected areas"_span, statusText); + return false; + } + + if (reqSize == 0) + { + return true; + } + } + + // If there are less than 2 supported maps, any combination of areas is valid. + if (!GetInstance()->HasFeature(Feature::kMaps) || GetNumberOfSupportedMaps() <= 1) + { + return true; + } + + // Check that all the areas are in the same map. + auto newAreasIter = req.newAreas.begin(); + newAreasIter.Next(); + + AreaStructureWrapper tempArea; + uint32_t ignoredIndex; + if (!GetSupportedAreaById(newAreasIter.GetValue(), ignoredIndex, tempArea)) + { + areaStatus = SelectAreasStatus::kUnsupportedArea; + CopyCharSpanToMutableCharSpan("unable to find selected area in supported areas"_span, statusText); + return false; + } + + auto mapId = tempArea.mapID.Value(); // It is safe to call `.Value()` as we confirmed that there are at least 2 maps. + + while (newAreasIter.Next()) + { + if (!GetSupportedAreaById(newAreasIter.GetValue(), ignoredIndex, tempArea)) + { + areaStatus = SelectAreasStatus::kUnsupportedArea; + CopyCharSpanToMutableCharSpan("unable to find selected area in supported areas"_span, statusText); + return false; + } + + if (tempArea.mapID.Value() != mapId) + { + areaStatus = SelectAreasStatus::kInvalidSet; + CopyCharSpanToMutableCharSpan("all selected areas must be in the same map"_span, statusText); + return false; + } + } + + if (CHIP_NO_ERROR != newAreasIter.GetStatus()) + { + areaStatus = SelectAreasStatus::kInvalidSet; + CopyCharSpanToMutableCharSpan("error processing new areas."_span, statusText); + return false; + } + return true; }; -bool RvcServiceAreaDelegate::HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan skipStatusText) +bool RvcServiceAreaDelegate::HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan & skipStatusText) { - // TODO IMPLEMENT - return true; + return (mHandleSkipCurrentAreaDeviceInstance->*mHandleSkipCurrentAreaCallback)(skippedArea, skipStatusText); }; //************************************************************************* @@ -101,8 +190,7 @@ bool RvcServiceAreaDelegate::HandleSkipCurrentArea(uint32_t skippedArea, Mutable bool RvcServiceAreaDelegate::IsSupportedAreasChangeAllowed() { - // TODO IMPLEMENT - return true; + return (mIsSupportedAreasChangeAllowedDeviceInstance->*mIsSupportedAreasChangeAllowedCallback)(); } uint32_t RvcServiceAreaDelegate::GetNumberOfSupportedAreas() @@ -196,8 +284,7 @@ bool RvcServiceAreaDelegate::ClearSupportedAreas() bool RvcServiceAreaDelegate::IsSupportedMapChangeAllowed() { - // TODO IMPLEMENT - return true; + return (mIsSupportedMapChangeAllowedDeviceInstance->*mIsSupportedMapChangeAllowedCallback)(); } uint32_t RvcServiceAreaDelegate::GetNumberOfSupportedMaps() @@ -397,8 +484,15 @@ bool RvcServiceAreaDelegate::AddProgressElement(const Structs::ProgressStruct::T bool RvcServiceAreaDelegate::ModifyProgressElement(uint32_t listIndex, const Structs::ProgressStruct::Type & modifiedProgressElement) { - // TODO IMPLEMENT - return false; + if (modifiedProgressElement.areaID != mProgressList[listIndex].areaID) + { + ChipLogError(Zcl, "ModifyProgressElement - areaID's do not match, new areaID %u, existing areaID %u", + modifiedProgressElement.areaID, mProgressList[listIndex].areaID); + return false; + } + + mProgressList[listIndex] = modifiedProgressElement; + return true; } bool RvcServiceAreaDelegate::ClearProgress() @@ -411,3 +505,125 @@ bool RvcServiceAreaDelegate::ClearProgress() return false; } + +void RvcServiceAreaDelegate::SetAttributesAtCleanStart() +{ + if (GetNumberOfSupportedAreas() == 0) + { + return; + } + + if (GetNumberOfSelectedAreas() == 0) + { + AreaStructureWrapper firstArea; + GetSupportedAreaByIndex(0, firstArea); + + GetInstance()->SetCurrentArea(firstArea.areaID); + + if (GetInstance()->HasFeature(Feature::kProgressReporting)) + { + GetInstance()->AddPendingProgressElement(firstArea.areaID); + GetInstance()->SetProgressStatus(firstArea.areaID, OperationalStatusEnum::kOperating); + } + } + else + { + uint32_t areaId; + GetSelectedAreaByIndex(0, areaId); + + GetInstance()->SetCurrentArea(areaId); + + if (GetInstance()->HasFeature(Feature::kProgressReporting)) + { + GetInstance()->AddPendingProgressElement(areaId); + GetInstance()->SetProgressStatus(areaId, OperationalStatusEnum::kOperating); + + uint32_t i = 1; + while (GetSelectedAreaByIndex(i, areaId)) + { + GetInstance()->AddPendingProgressElement(areaId); + i++; + } + } + } +} + +void RvcServiceAreaDelegate::GoToNextArea(OperationalStatusEnum currentAreaOpState, bool & finished) +{ + AreaStructureWrapper currentArea; + auto currentAreaIdN = GetInstance()->GetCurrentArea(); + + if (currentAreaIdN.IsNull()) + { + ChipLogError(Zcl, "GoToNextArea: Cannot go to the next area when the current area is null."); + return; + } + + if (currentAreaOpState != OperationalStatusEnum::kCompleted && currentAreaOpState != OperationalStatusEnum::kSkipped) + { + ChipLogError(Zcl, "GoToNextArea: currentAreaOpState must be either completed or skipped."); + return; + } + + auto currentAreaId = currentAreaIdN.Value(); + uint32_t currentAreaIndex; + GetSupportedAreaById(currentAreaId, currentAreaIndex, currentArea); + auto currentAreaMapId = currentArea.mapID; + finished = true; + + if (GetInstance()->HasFeature(Feature::kProgressReporting)) + { + GetInstance()->SetProgressStatus(currentAreaId, currentAreaOpState); + } + + if (GetNumberOfSelectedAreas() == 0) + { + AreaStructureWrapper nextArea; + uint32_t nextIndex = currentAreaIndex + 1; + while (GetSupportedAreaByIndex(nextIndex, nextArea)) + { + if (!currentAreaMapId.IsNull() && nextArea.mapID == currentAreaMapId.Value()) + { + GetInstance()->SetCurrentArea(nextArea.areaID); + + if (GetInstance()->HasFeature(Feature::kProgressReporting)) + { + GetInstance()->SetProgressStatus(nextArea.areaID, OperationalStatusEnum::kOperating); + } + + finished = false; + return; + } + + ++nextIndex; + } + } + else + { + uint32_t selectedAreaId; + uint32_t selectedAreaIndex = 0; + while (GetSelectedAreaByIndex(selectedAreaIndex, selectedAreaId)) + { + if (selectedAreaId == currentAreaId) + { + break; + } + ++selectedAreaIndex; + } + + uint32_t nextSelectedAreaId; + uint32_t nextSelectedAreaIndex = selectedAreaIndex + 1; + if (GetSelectedAreaByIndex(nextSelectedAreaIndex, nextSelectedAreaId)) + { + GetInstance()->SetCurrentArea(nextSelectedAreaId); + + if (GetInstance()->HasFeature(Feature::kProgressReporting)) + { + GetInstance()->SetProgressStatus(nextSelectedAreaId, OperationalStatusEnum::kOperating); + } + + finished = false; + return; + } + } +} diff --git a/src/app/clusters/service-area-server/service-area-delegate.h b/src/app/clusters/service-area-server/service-area-delegate.h index e31abf98f9e745..01df2faeb06488 100644 --- a/src/app/clusters/service-area-server/service-area-delegate.h +++ b/src/app/clusters/service-area-server/service-area-delegate.h @@ -72,7 +72,7 @@ class Delegate * @note The statusText field SHOULD indicate why the request is not allowed, given the current mode * of the device, which may involve other clusters. */ - virtual bool IsSetSelectedAreasAllowed(MutableCharSpan statusText) = 0; + virtual bool IsSetSelectedAreasAllowed(MutableCharSpan & statusText) = 0; /** * Given a set of locations to be set to the SelectedAreas attribute, this method should check that @@ -92,7 +92,7 @@ class Delegate * device must stop. */ virtual bool IsValidSelectAreasSet(const Commands::SelectAreas::DecodableType & req, SelectAreasStatus & locationStatus, - MutableCharSpan statusText) = 0; + MutableCharSpan & statusText) = 0; /** * @brief The server instance ensures that the SelectedAreas and CurrentArea attributes are not null before @@ -104,24 +104,23 @@ class Delegate * * @note skipStatusText must be filled out by the function on failure. * - * @note If the device successfully accepts the request and the ListOrder feature is set to 1: - * The server SHALL stop operating at the current location. - * The server SHALL attempt to operate at the remaining locations on the SelectedAreas attribute list, starting with - * the next entry. If the end of the SelectedAreas attribute list is reached, the server SHALL stop operating. - * - * @note If the device successfully accepts the request and the ListOrder feature is set to 0: - * The server SHALL stop operating at the current location. - * The server SHALL attempt to operate at the locations on the SelectedAreas attribute list where operating has not - * been completed, using a vendor defined order. If the server has completed operating at all locations on the SelectedAreas - * attribute list, the server SHALL stop operating. + * @note If the device accepts the request: + * - If the device is currently operating at the area identified by SkippedArea, as indicated by either the CurrentArea or + * the Progress attributes, if implemented, the device SHALL stop operating at that area. + * - If the Progress attribute is implemented, the entry corresponding to SkippedArea SHALL be updated to indicate that the + * area was skipped. + * - The server SHALL attempt to operate only at the areas in the SelectedAreas attribute list where operating has not been + * skipped or completed, using a vendor defined order. + * - If the server has either skipped or completed operating at all areas on the SelectedAreas attribute list, the server + * SHALL stop operating. * * @note If the Status field is set to InvalidAreaList, the StatusText field SHALL be an empty string. * If the Status field is not set to Success, or InvalidAreaList, the StatusText field SHALL include a vendor defined - * error description which can be used to explain the error to the user. For example, if the Status field is set to - * InvalidInMode, the StatusText field SHOULD indicate why the request is not allowed, given the current mode of the device, - * which may involve other clusters. + * error description which can be used to explain the error to the user. For example, if the Status field is set to + * InvalidInMode, the StatusText field SHOULD indicate why the request is not allowed, given the current mode of the + * device, which may involve other clusters. */ - virtual bool HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan skipStatusText) + virtual bool HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan & skipStatusText) { // device support of this command is optional CopyCharSpanToMutableCharSpan("Skip Current Area command not supported by device"_span, skipStatusText); @@ -262,7 +261,7 @@ class Delegate /** * This method is called by the server instance to modify an existing map in the list. * The server instance will ensure that the modifiedMap is a valid, unique map. - * @param[in] listIndexThe index of the map being modified. + * @param[in] listIndex The index of the map being modified. * @param[in] modifiedMapA map with the modified contents. * @return true if successful, false otherwise. * diff --git a/src/app/clusters/service-area-server/service-area-server.cpp b/src/app/clusters/service-area-server/service-area-server.cpp index d745c04b5d468e..0e562c78754d5b 100644 --- a/src/app/clusters/service-area-server/service-area-server.cpp +++ b/src/app/clusters/service-area-server/service-area-server.cpp @@ -399,6 +399,8 @@ void Instance::HandleSkipCurrentAreaCmd(HandlerContext & ctx, const Commands::Sk exitResponse(SkipAreaStatus::kInvalidInMode, skipStatusText); return; } + + exitResponse(SkipAreaStatus::kSuccess, ""_span); } //************************************************************************* @@ -469,35 +471,28 @@ bool Instance::IsValidSupportedArea(const AreaStructureWrapper & aArea) } // The mapID field SHALL be null if SupportedMaps is not supported or SupportedMaps is an empty list. - bool shouldMapsBeNull = false; - if (mFeature.Has(Feature::kMaps)) + if (mFeature.Has(Feature::kMaps) && (mDelegate->GetNumberOfSupportedMaps() > 0)) { - if (mDelegate->GetNumberOfSupportedMaps() == 0) + if (aArea.mapID.IsNull()) { - shouldMapsBeNull = true; + ChipLogDetail(Zcl, "IsValidSupportedArea %u - map Id should not be null when there are supported maps", aArea.areaID); + return false; } - } - else - { - shouldMapsBeNull = true; - } - if (shouldMapsBeNull) - { - if (!aArea.mapID.IsNull()) + // If the SupportedMaps attribute is not null, mapID SHALL be the ID of an entry from the SupportedMaps attribute. + if (!IsSupportedMap(aArea.mapID.Value())) { - ChipLogDetail(Zcl, "IsValidSupportedArea %u - map Id %u is not in empty supported map list", aArea.areaID, - aArea.mapID.Value()); + ChipLogError(Zcl, "IsValidSupportedArea %u - map Id %u is not in supported map list", aArea.areaID, + aArea.mapID.Value()); return false; } } else { - // If the SupportedMaps attribute is not null, mapID SHALL be the ID of an entry from the SupportedMaps attribute. - if (!IsSupportedMap(aArea.mapID.Value())) + if (!aArea.mapID.IsNull()) { - ChipLogError(Zcl, "IsValidSupportedArea %u - map Id %u is not in supported map list", aArea.areaID, - aArea.mapID.Value()); + ChipLogDetail(Zcl, "IsValidSupportedArea %u - map Id %u is not in empty supported map list", aArea.areaID, + aArea.mapID.Value()); return false; } } @@ -1005,7 +1000,7 @@ bool Instance::SetProgressStatus(uint32_t aAreaId, OperationalStatusEnum opStatu // TotalOperationalTime SHALL be null if the Status field is not set to Completed or Skipped. if ((opStatus != OperationalStatusEnum::kCompleted) && (opStatus != OperationalStatusEnum::kSkipped)) { - progressElement.totalOperationalTime.Value().SetNull(); + progressElement.totalOperationalTime.Emplace(DataModel::NullNullable); } // add the updated element to the progress attribute