/* * * Copyright (c) 2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include using namespace chip; using namespace chip::app::Clusters; using namespace chip::app::Clusters::ServiceArea; void RvcServiceAreaDelegate::SetMapTopology() { GetInstance()->ClearSupportedMaps(); GetInstance()->AddSupportedMap(supportedMapId_XX, "My Map XX"_span); GetInstance()->AddSupportedMap(supportedMapId_YY, "My Map YY"_span); // Area A has name, floor number, uses map XX auto areaA = AreaStructureWrapper{} .SetAreaId(supportedAreaID_A) .SetMapId(supportedMapId_XX) .SetLocationInfo("My Location A"_span, DataModel::Nullable(4), DataModel::Nullable()); // Area B has name, uses map XX auto areaB = AreaStructureWrapper{} .SetAreaId(supportedAreaID_B) .SetMapId(supportedMapId_XX) .SetLocationInfo("My Location B"_span, DataModel::NullNullable, DataModel::NullNullable); // Area C has full SemData, no name, Map YY auto areaC = AreaStructureWrapper{} .SetAreaId(supportedAreaID_C) .SetMapId(supportedMapId_YY) .SetLocationInfo(""_span, -1, Globals::AreaTypeTag::kPlayRoom) .SetLandmarkInfo(Globals::LandmarkTag::kBackDoor, Globals::RelativePositionTag::kNextTo); // Area D has null values for all landmark fields, Map YY auto areaD = AreaStructureWrapper{} .SetAreaId(supportedAreaID_D) .SetMapId(supportedMapId_YY) .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); } void RvcServiceAreaDelegate::SetNoMapTopology() { GetInstance()->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; } //************************************************************************* // command support bool RvcServiceAreaDelegate::IsSetSelectedAreasAllowed(MutableCharSpan & statusText) { return (mIsSetSelectedAreasAllowedDeviceInstance->*mIsSetSelectedAreasAllowedCallback)(statusText); }; bool RvcServiceAreaDelegate::IsValidSelectAreasSet(const Span & selectedAreas, SelectAreasStatus & areaStatus, MutableCharSpan & statusText) { if (selectedAreas.empty()) { return true; } // If there is 1 or 0 supported maps, any combination of areas is valid. if (!GetInstance()->HasFeature(Feature::kMaps) || GetInstance()->GetNumberOfSupportedMaps() <= 1) { return true; } // Check that all the requested areas are in the same map. { AreaStructureWrapper tempArea; uint32_t ignoredIndex; if (!GetInstance()->GetSupportedAreaById(selectedAreas[0], ignoredIndex, tempArea)) { areaStatus = SelectAreasStatus::kUnsupportedArea; CopyCharSpanToMutableCharSpanWithTruncation("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. for (const auto & areaId : selectedAreas.SubSpan(1)) { if (!GetInstance()->GetSupportedAreaById(areaId, ignoredIndex, tempArea)) { areaStatus = SelectAreasStatus::kUnsupportedArea; CopyCharSpanToMutableCharSpanWithTruncation("unable to find selected area in supported areas"_span, statusText); return false; } if (tempArea.mapID.Value() != mapId) { areaStatus = SelectAreasStatus::kInvalidSet; CopyCharSpanToMutableCharSpanWithTruncation("all selected areas must be in the same map"_span, statusText); return false; } } } return true; }; bool RvcServiceAreaDelegate::HandleSkipArea(uint32_t skippedArea, MutableCharSpan & skipStatusText) { return (mHandleSkipAreaDeviceInstance->*mHandleSkipAreaCallback)(skippedArea, skipStatusText); }; bool RvcServiceAreaDelegate::IsSupportedAreasChangeAllowed() { return (mIsSupportedAreasChangeAllowedDeviceInstance->*mIsSupportedAreasChangeAllowedCallback)(); } bool RvcServiceAreaDelegate::IsSupportedMapChangeAllowed() { return (mIsSupportedMapChangeAllowedDeviceInstance->*mIsSupportedMapChangeAllowedCallback)(); } void RvcServiceAreaDelegate::SetAttributesAtCleanStart() { if (GetInstance()->GetNumberOfSupportedAreas() == 0) { return; } if (GetInstance()->GetNumberOfSelectedAreas() == 0) { AreaStructureWrapper firstArea; GetInstance()->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; GetInstance()->GetSelectedAreaByIndex(0, areaId); GetInstance()->SetCurrentArea(areaId); if (GetInstance()->HasFeature(Feature::kProgressReporting)) { GetInstance()->AddPendingProgressElement(areaId); GetInstance()->SetProgressStatus(areaId, OperationalStatusEnum::kOperating); uint32_t i = 1; while (GetInstance()->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; GetInstance()->GetSupportedAreaById(currentAreaId, currentAreaIndex, currentArea); auto currentAreaMapId = currentArea.mapID; finished = true; if (GetInstance()->HasFeature(Feature::kProgressReporting)) { GetInstance()->SetProgressStatus(currentAreaId, currentAreaOpState); } if (GetInstance()->GetNumberOfSelectedAreas() == 0) { AreaStructureWrapper nextArea; uint32_t nextIndex = currentAreaIndex + 1; while (GetInstance()->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 (GetInstance()->GetSelectedAreaByIndex(selectedAreaIndex, selectedAreaId)) { if (selectedAreaId == currentAreaId) { break; } ++selectedAreaIndex; } uint32_t nextSelectedAreaId; uint32_t nextSelectedAreaIndex = selectedAreaIndex + 1; if (GetInstance()->GetSelectedAreaByIndex(nextSelectedAreaIndex, nextSelectedAreaId)) { GetInstance()->SetCurrentArea(nextSelectedAreaId); if (GetInstance()->HasFeature(Feature::kProgressReporting)) { GetInstance()->SetProgressStatus(nextSelectedAreaId, OperationalStatusEnum::kOperating); } finished = false; return; } } }