Skip to content

Commit

Permalink
[IM] Implementing PathIterator for iterating attribute paths with wil…
Browse files Browse the repository at this point in the history
…dcard fields
  • Loading branch information
erjiaqing committed Nov 5, 2021
1 parent b18ecb9 commit f72a610
Show file tree
Hide file tree
Showing 11 changed files with 915 additions and 5 deletions.
193 changes: 193 additions & 0 deletions src/app/AttributePathExpandIterator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
*
* Copyright (c) 2021 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.
*/

/**
* @file
* This file defines read handler for a CHIP Interaction Data model
*
*/

#include <app/AttributePathExpandIterator.h>

#include <app-common/zap-generated/att-storage.h>
#include <app/ClusterInfo.h>
#include <app/ConcreteAttributePath.h>
#include <app/EventManagement.h>
#include <app/InteractionModelDelegate.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPTLVDebug.hpp>
#include <lib/support/CodeUtils.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/logging/CHIPLogging.h>

using namespace chip;

// TODO: Here we use forward declaration for these symbols used, there should be some reorganize for code in app/util so they can be
// used with generated files or some mock files.
typedef uint8_t EmberAfClusterMask;
#define CLUSTER_MASK_SERVER (0x40)

extern uint16_t emberAfEndpointCount(void);
extern uint16_t emberAfIndexFromEndpoint(EndpointId endpoint);
extern uint8_t emberAfClusterCount(EndpointId endpoint, bool server);
extern uint16_t emberAfGetServerAttributeCount(chip::EndpointId endpoint, chip::ClusterId cluster);
extern uint16_t emberAfGetServerAttributeIndexByAttributeId(chip::EndpointId endpoint, chip::ClusterId cluster,
chip::AttributeId attributeId);
extern chip::EndpointId emberAfEndpointFromIndex(uint16_t index);
extern Optional<ClusterId> emberAfGetNthClusterId(chip::EndpointId endpoint, uint8_t n, bool server);
extern Optional<AttributeId> emberAfGetServerAttributeIdByIndex(chip::EndpointId endpoint, chip::ClusterId cluster,
uint16_t attributeIndex);
extern uint8_t emberAfClusterIndex(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask);

namespace chip {
namespace app {

void AttributePathExpandIterator::Reset(ClusterInfo * aClusterInfo)
{
mpClusterInfo = aClusterInfo;

// Reset iterator state
mEndpointIndex = UINT16_MAX;
mClusterIndex = UINT8_MAX;
mAttributeIndex = UINT16_MAX;

Proceed();
}

void AttributePathExpandIterator::PrepareEndpointIndexRange(const ClusterInfo & aClusterInfo)
{
if (aClusterInfo.HasWildcardEndpointId())
{
mBeginEndpointIndex = 0;
mEndEndpointIndex = emberAfEndpointCount();
}
else
{
mBeginEndpointIndex = emberAfIndexFromEndpoint(aClusterInfo.mEndpointId);
// If the given cluster id does not exist on the given endpoint, it will return uint16(0xFFFF), then endEndpointIndex
// will be 0, means we should iterate a null endppint set (skip it).
mEndEndpointIndex = static_cast<uint16_t>(mBeginEndpointIndex + 1);
}
}

void AttributePathExpandIterator::PrepareClusterIndexRange(const ClusterInfo & aClusterInfo, EndpointId aEndpointId)
{
if (aClusterInfo.HasWildcardClusterId())
{
mBeginClusterIndex = 0;
mEndClusterIndex = emberAfClusterCount(aEndpointId, true /* server */);
}
else
{
mBeginClusterIndex = emberAfClusterIndex(aEndpointId, aClusterInfo.mClusterId, CLUSTER_MASK_SERVER);
// If the given cluster id does not exist on the given endpoint, it will return uint8(0xFF), then endClusterIndex
// will be 0, means we should i
mEndClusterIndex = static_cast<uint8_t>(mBeginClusterIndex + 1);
}
}

void AttributePathExpandIterator::PrepareAttributeIndexRange(const ClusterInfo & aClusterInfo, EndpointId aEndpointId,
ClusterId aClusterId)
{
if (aClusterInfo.HasWildcardAttributeId())
{
mBeginAttributeIndex = 0;
mEndAttributeIndex = emberAfGetServerAttributeCount(aEndpointId, aClusterId);
}
else
{
mBeginAttributeIndex = emberAfGetServerAttributeIndexByAttributeId(aEndpointId, aClusterId, aClusterInfo.mFieldId);
// If the given attribute id does not exist on the given endpoint, it will return uint16(0xFFFF), then endAttributeIndex
// will be 0
mEndAttributeIndex = static_cast<uint16_t>(mBeginAttributeIndex + 1);
}
}

bool AttributePathExpandIterator::Proceed()
{
for (; mpClusterInfo != nullptr; (mpClusterInfo = mpClusterInfo->mpNext, mEndpointIndex = UINT16_MAX))
{
// Special case: If this is a concrete path, we just return its value as-is.
if (!mpClusterInfo->HasWildcard())
{
mOutputPath.mEndpointId = mpClusterInfo->mEndpointId;
mOutputPath.mClusterId = mpClusterInfo->mClusterId;
mOutputPath.mAttributeId = mpClusterInfo->mFieldId;

// Prepare for next iteration
(mpClusterInfo = mpClusterInfo->mpNext, mEndpointIndex = UINT16_MAX);
return true;
}

if (mEndpointIndex == UINT16_MAX)
{
PrepareEndpointIndexRange(*mpClusterInfo);
// If we have not started iterating over the endpoints yet.
mEndpointIndex = mBeginEndpointIndex;
mClusterIndex = UINT8_MAX;
}

for (; mEndpointIndex < mEndEndpointIndex; (mEndpointIndex++, mClusterIndex = UINT8_MAX, mAttributeIndex = UINT16_MAX))
{
EndpointId endpointId = emberAfEndpointFromIndex(mEndpointIndex);

if (mClusterIndex == UINT8_MAX)
{
PrepareClusterIndexRange(*mpClusterInfo, endpointId);
// If we have not started iterating over the clusters yet.
mClusterIndex = mBeginClusterIndex;
mAttributeIndex = UINT16_MAX;
}

for (; mClusterIndex < mEndClusterIndex; (mClusterIndex++, mAttributeIndex = UINT16_MAX))
{
// emberAfGetNthClusterId must return a valid cluster id here since we have verified the mClusterIndex does
// not exceed the endAttributeIndex.
ClusterId clusterId = emberAfGetNthClusterId(endpointId, mClusterIndex, true /* server */).Value();
if (mAttributeIndex == UINT16_MAX)
{
PrepareAttributeIndexRange(*mpClusterInfo, endpointId, clusterId);
// If we have not started iterating over the attributes yet.
mAttributeIndex = mBeginAttributeIndex;
}

if (mAttributeIndex < mEndAttributeIndex)
{
// GetServerAttributeIdByIdex must return a valid attribute here since we have verified the mAttributeindex does
// not exceed the endAttributeIndex.
mOutputPath.mAttributeId = emberAfGetServerAttributeIdByIndex(endpointId, clusterId, mAttributeIndex).Value();
mOutputPath.mClusterId = clusterId;
mOutputPath.mEndpointId = endpointId;
mAttributeIndex++;
// We found a valid attribute path, now return and increase the attribute index for next iteration.
// Return true will skip the increment of mClusterIndex, mEndpointIndex and mpClusterInfo.
return true;
}
// We have exhausted all attributes of this cluster, continue iterating over attributes of next cluster.
}
// We have exhausted all clusters of this endpoint, continue iterating over clusters of next endpoint.
}
// We have exhausted all endpoints in this cluster info, continue iterating over next cluster info item.
}

// Reset to default, invalid value.
mOutputPath = ConcreteAttributePath();
return false;
}
} // namespace app
} // namespace chip
100 changes: 100 additions & 0 deletions src/app/AttributePathExpandIterator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
*
* Copyright (c) 2021 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.
*/

#pragma once

#include <app/ClusterInfo.h>
#include <app/ConcreteAttributePath.h>
#include <app/EventManagement.h>
#include <app/InteractionModelDelegate.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPTLVDebug.hpp>
#include <lib/support/CodeUtils.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/logging/CHIPLogging.h>
#include <messaging/ExchangeContext.h>
#include <messaging/ExchangeMgr.h>
#include <messaging/Flags.h>
#include <protocols/Protocols.h>
#include <system/SystemPacketBuffer.h>

namespace chip {
namespace app {

/**
* AttributePathExpandIterator is used for iterate over a linked list of ClusterInfo-s.
* The AttributePathExpandIterator is copiable, however, the given cluster info must be valid when calling proceed.
*
* AttributePathExpandIterator will expand attribute paths with wildcards, and only emit exist paths for ClusterInfo with
* wildcards. For ClusterInfo with a concrete path (i.e. does not contain wildcards), AttributePathExpandIterator will emit them
* as-is.
*
* The typical use of AttributePathExpandIterator may look like:
* ConcreteAttributePath path;
* for (AttributePathExpandIterator iterator(clusterInfo); iterator.Get(path); iterator.Proceed()) {...}
*
* If AttributePathExpandIterator is called with a invalid ClusterInfo (e.g. a freed ClusterInfo), the program may misbehave.
* If there are new endpoints, clusters or attributes adds, AttributePathExpandIterator must be reseted or it may emit unexpected
* value.
*/
class AttributePathExpandIterator
{
public:
AttributePathExpandIterator(ClusterInfo * aClusterInfo) { Reset(aClusterInfo); }

/**
* Initialize / reset the state of the path iterator.
* Get() will return the first valid path in the first ClusterInfo.
* Passing a nullptr to Reset will clear the state of the AttributePathExpandIterator.
*/
void Reset(ClusterInfo * aClusterInfo);

/**
* Proceed the iterator to the next attribute path in the given cluster info.
*
* Feturns false if AttributePathExpandIterator has exhausted all paths in the given ClusterInfo list.
*/
bool Proceed();

/**
* Fills the aPath with the path the iterator current points to.
* Return false if the iterator is not pointing a valid path (i.e. it has exhausted the cluster info).
*/
bool Get(ConcreteAttributePath & aPath)
{
aPath = mOutputPath;
return mOutputPath.mEndpointId != ConcreteAttributePath::kInvalidEndpointId;
}

bool Valid() { return mpClusterInfo != nullptr; }

private:
ClusterInfo * mpClusterInfo;

uint16_t mEndpointIndex, mBeginEndpointIndex, mEndEndpointIndex;
uint8_t mClusterIndex, mBeginClusterIndex, mEndClusterIndex;
uint16_t mAttributeIndex, mBeginAttributeIndex, mEndAttributeIndex;

ConcreteAttributePath mOutputPath = ConcreteAttributePath();

void PrepareEndpointIndexRange(const ClusterInfo & aClusterInfo);
void PrepareClusterIndexRange(const ClusterInfo & aClusterInfo, EndpointId aEndpointId);
void PrepareAttributeIndexRange(const ClusterInfo & aClusterInfo, EndpointId aEndpointId, ClusterId aClusterId);
};
} // namespace app
} // namespace chip
3 changes: 3 additions & 0 deletions src/app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ static_library("app") {
output_name = "libCHIPDataModel"

sources = [
"AttributePathExpandIterator.cpp",
"AttributePathExpandIterator.h",
"AttributePathParams.h",
"Command.cpp",
"Command.h",
"CommandHandler.cpp",
Expand Down
11 changes: 8 additions & 3 deletions src/app/ConcreteAttributePath.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ namespace app {
*/
struct ConcreteAttributePath
{
static constexpr EndpointId kInvalidEndpointId = 0xFFFF;
static constexpr ClusterId kInvalidClusterId = 0xFFFF'FFFF;
static constexpr AttributeId kInvalidAttributeId = 0xFFFF'FFFF;

ConcreteAttributePath() {}
ConcreteAttributePath(EndpointId aEndpointId, ClusterId aClusterId, AttributeId aAttributeId) :
mEndpointId(aEndpointId), mClusterId(aClusterId), mAttributeId(aAttributeId)
{}
Expand All @@ -37,9 +42,9 @@ struct ConcreteAttributePath
return mEndpointId == other.mEndpointId && mClusterId == other.mClusterId && mAttributeId == other.mAttributeId;
}

EndpointId mEndpointId = 0;
ClusterId mClusterId = 0;
AttributeId mAttributeId = 0;
EndpointId mEndpointId = kInvalidEndpointId;
ClusterId mClusterId = kInvalidClusterId;
AttributeId mAttributeId = kInvalidAttributeId;
};
} // namespace app
} // namespace chip
2 changes: 2 additions & 0 deletions src/app/tests/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ chip_test_suite("tests") {
output_name = "libAppTests"

test_sources = [
"TestAttributePathExpandIterator.cpp",
"TestAttributeValueEncoder.cpp",
"TestBuilderParser.cpp",
"TestCHIPDeviceCallbacksMgr.cpp",
Expand All @@ -65,6 +66,7 @@ chip_test_suite("tests") {
"${chip_root}/src/app/common:cluster-objects",
"${chip_root}/src/app/tests:helpers",
"${chip_root}/src/app/util:device_callbacks_manager",
"${chip_root}/src/app/util/mock:mock_ember",
"${chip_root}/src/lib/core",
"${nlunit_test_root}:nlunit-test",
]
Expand Down
Loading

0 comments on commit f72a610

Please sign in to comment.