blob: 1a6ba6d8c95999aa90a37c3590edc15a0a8ee1e4 [file] [log] [blame]
/*
*
* 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 <app-common/zap-generated/attributes/Accessors.h>
#include <rvc-service-area-delegate.h>
#include <vector>
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<int16_t>(4), DataModel::Nullable<Globals::AreaTypeTag>());
// 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<int16_t>(4), DataModel::Nullable<Globals::AreaTypeTag>());
// 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<const uint32_t> & 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;
}
}
}