From 90c32310da1c7bbefa5080bfae6700e46d8ce3ad Mon Sep 17 00:00:00 2001 From: Song Guo Date: Thu, 28 Oct 2021 12:43:37 +0800 Subject: [PATCH 1/2] [im] Fix wildcard path support per spec --- src/app/ClusterInfo.h | 103 ++++-------------- src/app/EventManagement.cpp | 6 +- src/app/InteractionModelEngine.cpp | 2 - src/app/MessageDef/AttributePath.cpp | 26 +++++ src/app/MessageDef/AttributePath.h | 51 +++++++++ src/app/MessageDef/EventPath.cpp | 20 ++++ src/app/MessageDef/EventPath.h | 40 +++++++ src/app/MessageDef/Parser.h | 18 +++ src/app/ReadClient.cpp | 46 ++------ src/app/ReadHandler.cpp | 63 ++++------- src/app/WriteHandler.cpp | 37 ++----- src/app/reporting/Engine.cpp | 18 +-- src/app/tests/TestClusterInfo.cpp | 44 ++++---- src/app/tests/TestEventLogging.cpp | 16 +-- src/app/tests/TestInteractionModelEngine.cpp | 22 ++-- src/app/tests/TestReadInteraction.cpp | 38 +++---- src/app/tests/TestWriteInteraction.cpp | 5 +- .../tests/integration/chip_im_initiator.cpp | 3 +- .../tests/integration/chip_im_responder.cpp | 9 +- .../util/ember-compatibility-functions.cpp | 23 ++-- src/app/util/im-client-callbacks.cpp | 18 ++- src/controller/CHIPDeviceController.cpp | 2 +- src/controller/TypedReadCallback.h | 5 +- .../chip/interaction_model/Delegate.cpp | 4 +- src/controller/tests/data_model/TestWrite.cpp | 10 +- 25 files changed, 335 insertions(+), 294 deletions(-) diff --git a/src/app/ClusterInfo.h b/src/app/ClusterInfo.h index f9179ab373db72..01dc5feeacb3d0 100644 --- a/src/app/ClusterInfo.h +++ b/src/app/ClusterInfo.h @@ -25,96 +25,37 @@ namespace chip { namespace app { -static constexpr AttributeId kRootAttributeId = 0xFFFFFFFF; +/** + * ClusterInfo is the representation of an attribute path or an event path used by ReacHandler, ReadClient, WriteHandler, + * Report::Engine etc, it contains a mpNext field so it can be used as a linked list. It uses some invalid values for representing + * the wildcard value for its field. + */ +// TODO: The cluster info should be breaked into AttributeInfo and EventInfo. +// Note: The change will happen after #11171 with a better linked list. struct ClusterInfo { - enum class Flags : uint8_t - { - kFieldIdValid = 0x01, - kListIndexValid = 0x02, - kEventIdValid = 0x03, - }; - bool IsAttributePathSupersetOf(const ClusterInfo & other) const { - if ((other.mEndpointId != mEndpointId) || (other.mClusterId != mClusterId)) - { - return false; - } - - Optional myFieldId = - mFlags.Has(Flags::kFieldIdValid) ? Optional::Value(mFieldId) : Optional::Missing(); - - Optional otherFieldId = other.mFlags.Has(Flags::kFieldIdValid) ? Optional::Value(other.mFieldId) - : Optional::Missing(); - - Optional myListIndex = - mFlags.Has(Flags::kListIndexValid) ? Optional::Value(mListIndex) : Optional::Missing(); - - Optional otherListIndex = other.mFlags.Has(Flags::kListIndexValid) ? Optional::Value(other.mListIndex) - : Optional::Missing(); + VerifyOrReturnError(!mEndpointId.HasValue() || mEndpointId == other.mEndpointId, false); + VerifyOrReturnError(!mClusterId.HasValue() || mClusterId == other.mClusterId, false); + VerifyOrReturnError(!mFieldId.HasValue() || mFieldId == other.mFieldId, false); + VerifyOrReturnError(!mListIndex.HasValue() || mListIndex == other.mListIndex, false); - // If list index exists, field index must exist - // Field 0xFFFFFFF (any) & listindex set is invalid - assert(!(myListIndex.HasValue() && !myFieldId.HasValue())); - assert(!(otherListIndex.HasValue() && !otherFieldId.HasValue())); - assert(!(myFieldId == Optional::Value(kRootAttributeId) && myListIndex.HasValue())); - assert(!(otherFieldId == Optional::Value(kRootAttributeId) && otherListIndex.HasValue())); - - if (myFieldId == Optional::Value(kRootAttributeId)) - { - return true; - } - - if (myFieldId != otherFieldId) - { - return false; - } - - // We only support top layer for attribute representation, either FieldId or FieldId + ListIndex - // Combination: if myFieldId == otherFieldId, ListIndex cannot exist without FieldId - // 1. myListIndex and otherListIndex both missing or both exactly the same, then current is superset of other - // 2. myListIndex is missing, no matter if otherListIndex is missing or not, then current is superset of other - if (myListIndex == otherListIndex) - { - // either both missing or both exactly the same - return true; - } - - if (!myListIndex.HasValue()) - { - // difference is ok only if myListIndex is missing - return true; - } - - return false; + return true; } + bool HasWildcard() const { return !mEndpointId.HasValue() || !mClusterId.HasValue() || !mFieldId.HasValue(); } + ClusterInfo() {} - NodeId mNodeId = 0; - ClusterId mClusterId = 0; - ListIndex mListIndex = 0; - AttributeId mFieldId = 0; - EndpointId mEndpointId = 0; - BitFlags mFlags; - ClusterInfo * mpNext = nullptr; - EventId mEventId = 0; - /* For better structure alignment - * Above ordering is by bit-size to ensure least amount of memory alignment padding. - * Changing order to something more natural (e.g. clusterid before nodeid) will result - * in extra memory alignment padding. - * uint64 mNodeId - * uint16_t mClusterId - * uint16_t mListIndex - * uint8_t FieldId - * uint8_t EndpointId - * uint8_t mDirty - * uint8_t mType - * uint32_t mpNext - * uint16_t EventId - * padding 2 bytes - */ + + Optional mNodeId; + Optional mEndpointId; + Optional mClusterId; + Optional mFieldId; + Optional mEventId; + Optional mListIndex; + ClusterInfo * mpNext = nullptr; // pointer width (uint32 or uint64) }; } // namespace app } // namespace chip diff --git a/src/app/EventManagement.cpp b/src/app/EventManagement.cpp index 2df0de0f36945e..78420052314470 100644 --- a/src/app/EventManagement.cpp +++ b/src/app/EventManagement.cpp @@ -618,8 +618,10 @@ static bool IsInterestedEventPaths(EventLoadOutContext * eventLoadOutContext, co } while (interestedEventPaths != nullptr) { - if (interestedEventPaths->mNodeId == event.mNodeId && interestedEventPaths->mEndpointId == event.mEndpointId && - interestedEventPaths->mClusterId == event.mClusterId && interestedEventPaths->mEventId == event.mEventId) + if (interestedEventPaths->mNodeId == Optional(event.mNodeId) && + interestedEventPaths->mEndpointId == Optional(event.mEndpointId) && + interestedEventPaths->mClusterId == Optional(event.mClusterId) && + interestedEventPaths->mEventId == Optional(event.mEventId)) { return true; } diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 2c33beeec2487d..94a09cb1a3e887 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -471,7 +471,6 @@ void InteractionModelEngine::ReleaseClusterInfoList(ClusterInfo *& aClusterInfo) { lastClusterInfo = lastClusterInfo->mpNext; } - lastClusterInfo->mFlags.ClearAll(); lastClusterInfo->mpNext = mpNextAvailableClusterInfo; mpNextAvailableClusterInfo = aClusterInfo; aClusterInfo = nullptr; @@ -508,7 +507,6 @@ bool InteractionModelEngine::MergeOverlappedAttributePath(ClusterInfo * apAttrib { runner->mListIndex = aAttributePath.mListIndex; runner->mFieldId = aAttributePath.mFieldId; - runner->mFlags = aAttributePath.mFlags; return true; } runner = runner->mpNext; diff --git a/src/app/MessageDef/AttributePath.cpp b/src/app/MessageDef/AttributePath.cpp index 0489eb0695fe6a..a25134446d8c0d 100644 --- a/src/app/MessageDef/AttributePath.cpp +++ b/src/app/MessageDef/AttributePath.cpp @@ -29,6 +29,7 @@ #include #include +#include using namespace chip; using namespace chip::TLV; @@ -193,6 +194,31 @@ CHIP_ERROR AttributePath::Parser::GetListIndex(chip::ListIndex * const apListInd return GetUnsignedInteger(kCsTag_ListIndex, apListIndex); } +CHIP_ERROR AttributePath::Parser::GetNodeId(chip::Optional & aNodeId) const +{ + return GetUnsignedInteger(kCsTag_NodeId, aNodeId); +} + +CHIP_ERROR AttributePath::Parser::GetEndpointId(chip::Optional & aEndpointId) const +{ + return GetUnsignedInteger(kCsTag_EndpointId, aEndpointId); +} + +CHIP_ERROR AttributePath::Parser::GetClusterId(chip::Optional & aClusterId) const +{ + return GetUnsignedInteger(kCsTag_ClusterId, aClusterId); +} + +CHIP_ERROR AttributePath::Parser::GetFieldId(chip::Optional & aFieldId) const +{ + return GetUnsignedInteger(kCsTag_FieldId, aFieldId); +} + +CHIP_ERROR AttributePath::Parser::GetListIndex(chip::Optional & aListIndex) const +{ + return GetUnsignedInteger(kCsTag_ListIndex, aListIndex); +} + CHIP_ERROR AttributePath::Builder::_Init(chip::TLV::TLVWriter * const apWriter, const Tag aTag) { mpWriter = apWriter; diff --git a/src/app/MessageDef/AttributePath.h b/src/app/MessageDef/AttributePath.h index 790581dd74b178..b0ce0dbc647c97 100644 --- a/src/app/MessageDef/AttributePath.h +++ b/src/app/MessageDef/AttributePath.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -84,6 +85,16 @@ class Parser : public chip::app::Parser */ CHIP_ERROR GetNodeId(chip::NodeId * const apNodeId) const; + /** + * @brief Get NodeId field, will set the aNodeId to Optional::Missing when value does not exist. + * + * @param [in] aListIndex The value for getting list index field. + * + * @return #CHIP_NO_ERROR on success + * #CHIP_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types + */ + CHIP_ERROR GetNodeId(chip::Optional & aNodeId) const; + /** * @brief Get a TLVReader for the EndpointId. Next() must be called before accessing them. * @@ -95,6 +106,16 @@ class Parser : public chip::app::Parser */ CHIP_ERROR GetEndpointId(chip::EndpointId * const apEndpointId) const; + /** + * @brief Gets EndpointId, will set the aEndpointId to Optional::Missing when the field is missing. + * + * @param [in] aListIndex The value for getting list index field. + * + * @return #CHIP_NO_ERROR on success + * #CHIP_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types + */ + CHIP_ERROR GetEndpointId(chip::Optional & aEndpointId) const; + /** * @brief Get a TLVReader for the ClusterId. Next() must be called before accessing them. * @@ -106,6 +127,16 @@ class Parser : public chip::app::Parser */ CHIP_ERROR GetClusterId(chip::ClusterId * const apClusterId) const; + /** + * @brief Gets ClusterId, will set the aClusterId to Optional::Missing when the field is missing. + * + * @param [in] aListIndex The value for getting list index field. + * + * @return #CHIP_NO_ERROR on success + * #CHIP_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types + */ + CHIP_ERROR GetClusterId(chip::Optional & aClusterId) const; + /** * @brief Get a TLVReader for the FieldId. Next() must be called before accessing them. * @@ -117,6 +148,16 @@ class Parser : public chip::app::Parser */ CHIP_ERROR GetFieldId(chip::AttributeId * const apFieldId) const; + /** + * @brief Gets FieldId, will set the aFieldId to Optional::Missing when the field is missing. + * + * @param [in] aListIndex The value for getting list index field. + * + * @return #CHIP_NO_ERROR on success + * #CHIP_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types + */ + CHIP_ERROR GetFieldId(chip::Optional & aFieldId) const; + /** * @brief Get a TLVReader for the ListIndex. Next() must be called before accessing them. * @@ -127,6 +168,16 @@ class Parser : public chip::app::Parser * #CHIP_END_OF_TLV if there is no such element */ CHIP_ERROR GetListIndex(chip::ListIndex * const apListIndex) const; + + /** + * @brief Gets ListIndex, will set the aListIndex to Optional::Missing when the field is missing. + * + * @param [in] aListIndex The value for getting list index field. + * + * @return #CHIP_NO_ERROR on success + * #CHIP_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types + */ + CHIP_ERROR GetListIndex(chip::Optional & aListIndex) const; }; class Builder : public chip::app::Builder diff --git a/src/app/MessageDef/EventPath.cpp b/src/app/MessageDef/EventPath.cpp index 5c134550b5af43..2bb002ab1b8abb 100644 --- a/src/app/MessageDef/EventPath.cpp +++ b/src/app/MessageDef/EventPath.cpp @@ -176,6 +176,26 @@ CHIP_ERROR EventPath::Parser::GetEventId(chip::EventId * const apEventId) const return GetUnsignedInteger(kCsTag_EventId, apEventId); } +CHIP_ERROR EventPath::Parser::GetNodeId(chip::Optional & aNodeId) const +{ + return GetUnsignedInteger(kCsTag_NodeId, aNodeId); +} + +CHIP_ERROR EventPath::Parser::GetEndpointId(chip::Optional & aEndpointId) const +{ + return GetUnsignedInteger(kCsTag_EndpointId, aEndpointId); +} + +CHIP_ERROR EventPath::Parser::GetClusterId(chip::Optional & aClusterId) const +{ + return GetUnsignedInteger(kCsTag_ClusterId, aClusterId); +} + +CHIP_ERROR EventPath::Parser::GetEventId(chip::Optional & aFieldId) const +{ + return GetUnsignedInteger(kCsTag_EventId, aFieldId); +} + CHIP_ERROR EventPath::Builder::_Init(chip::TLV::TLVWriter * const apWriter, const Tag aTag) { mpWriter = apWriter; diff --git a/src/app/MessageDef/EventPath.h b/src/app/MessageDef/EventPath.h index 6acec4fcff2982..04031b6f60123a 100644 --- a/src/app/MessageDef/EventPath.h +++ b/src/app/MessageDef/EventPath.h @@ -84,6 +84,16 @@ class Parser : public chip::app::Parser */ CHIP_ERROR GetNodeId(chip::NodeId * const apNodeId) const; + /** + * @brief Get NodeId field, will set the aNodeId to Optional::Missing when value does not exist. + * + * @param [in] aListIndex The value for getting list index field. + * + * @return #CHIP_NO_ERROR on success + * #CHIP_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types + */ + CHIP_ERROR GetNodeId(chip::Optional & aNodeId) const; + /** * @brief Get a TLVReader for the EndpointId. Next() must be called before accessing them. * @@ -95,6 +105,16 @@ class Parser : public chip::app::Parser */ CHIP_ERROR GetEndpointId(chip::EndpointId * const apEndpointId) const; + /* + * @brief Gets EndpointId, will set the aEndpointId to Optional::Missing when the field is missing. + * + * @param [in] aListIndex The value for getting list index field. + * + * @return #CHIP_NO_ERROR on success + * #CHIP_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types + */ + CHIP_ERROR GetEndpointId(chip::Optional & aEndpointId) const; + /** * @brief Get a TLVReader for the ClusterId. Next() must be called before accessing them. * @@ -106,6 +126,16 @@ class Parser : public chip::app::Parser */ CHIP_ERROR GetClusterId(chip::ClusterId * const apClusterId) const; + /** + * @brief Gets ClusterId, will set the aClusterId to Optional::Missing when the field is missing. + * + * @param [in] aListIndex The value for getting list index field. + * + * @return #CHIP_NO_ERROR on success + * #CHIP_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types + */ + CHIP_ERROR GetClusterId(chip::Optional & aClusterId) const; + /** * @brief Get a TLVReader for the EventId. Next() must be called before accessing them. * @@ -116,6 +146,16 @@ class Parser : public chip::app::Parser * #CHIP_END_OF_TLV if there is no such element */ CHIP_ERROR GetEventId(chip::EventId * const apEventId) const; + + /** + * @brief Gets FieldId, will set the aFieldId to Optional::Missing when the field is missing. + * + * @param [in] aListIndex The value for getting list index field. + * + * @return #CHIP_NO_ERROR on success + * #CHIP_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types + */ + CHIP_ERROR GetEventId(chip::Optional & aFieldId) const; }; class Builder : public chip::app::Builder diff --git a/src/app/MessageDef/Parser.h b/src/app/MessageDef/Parser.h index 0b027a93002474..3d213e3ea1d1d1 100644 --- a/src/app/MessageDef/Parser.h +++ b/src/app/MessageDef/Parser.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -73,6 +74,23 @@ class Parser return GetSimpleValue(aContextTag, chip::TLV::kTLVType_UnsignedInteger, apLValue); }; + template + CHIP_ERROR GetUnsignedInteger(const uint8_t aContextTag, Optional & aValue) const + { + T value; + CHIP_ERROR err = GetSimpleValue(aContextTag, chip::TLV::kTLVType_UnsignedInteger, &value); + aValue.ClearValue(); + if (err == CHIP_NO_ERROR) + { + aValue.SetValue(value); + } + if (err != CHIP_ERROR_END_OF_TLV) + { + return err; + } + return CHIP_NO_ERROR; + }; + template CHIP_ERROR GetSimpleValue(const uint8_t aContextTag, const chip::TLV::TLVType aTLVType, T * const apLValue) const { diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp index 0e7ab15fe6e33a..2deab777796ab7 100644 --- a/src/app/ReadClient.cpp +++ b/src/app/ReadClient.cpp @@ -477,40 +477,18 @@ CHIP_ERROR ReadClient::ProcessAttributeDataList(TLV::TLVReader & aAttributeDataL err = element.Init(reader); SuccessOrExit(err); - err = element.GetAttributePath(&attributePathParser); - SuccessOrExit(err); - - err = attributePathParser.GetNodeId(&(clusterInfo.mNodeId)); - SuccessOrExit(err); - - err = attributePathParser.GetEndpointId(&(clusterInfo.mEndpointId)); - SuccessOrExit(err); - - err = attributePathParser.GetClusterId(&(clusterInfo.mClusterId)); - SuccessOrExit(err); - - err = attributePathParser.GetFieldId(&(clusterInfo.mFieldId)); - if (CHIP_NO_ERROR == err) - { - clusterInfo.mFlags.Set(ClusterInfo::Flags::kFieldIdValid); - } - else if (CHIP_END_OF_TLV == err) - { - err = CHIP_NO_ERROR; - } - SuccessOrExit(err); - - err = attributePathParser.GetListIndex(&(clusterInfo.mListIndex)); - if (CHIP_NO_ERROR == err) - { - VerifyOrExit(clusterInfo.mFlags.Has(ClusterInfo::Flags::kFieldIdValid), err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH); - clusterInfo.mFlags.Set(ClusterInfo::Flags::kListIndexValid); - } - else if (CHIP_END_OF_TLV == err) - { - err = CHIP_NO_ERROR; - } - SuccessOrExit(err); + SuccessOrExit(err = element.GetAttributePath(&attributePathParser)); + + SuccessOrExit(err = attributePathParser.GetNodeId(clusterInfo.mNodeId)); + SuccessOrExit(err = attributePathParser.GetEndpointId(clusterInfo.mEndpointId)); + SuccessOrExit(err = attributePathParser.GetClusterId(clusterInfo.mClusterId)); + SuccessOrExit(err = attributePathParser.GetFieldId(clusterInfo.mFieldId)); + SuccessOrExit(err = attributePathParser.GetListIndex(clusterInfo.mListIndex)); + VerifyOrExit(!clusterInfo.mListIndex.HasValue() || clusterInfo.mFieldId.HasValue(), + err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH); + + VerifyOrExit(clusterInfo.mEndpointId.HasValue() && clusterInfo.mClusterId.HasValue() && clusterInfo.mFieldId.HasValue(), + err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH); err = element.GetData(&dataReader); if (err == CHIP_END_OF_TLV) diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 0383cfdc235961..91405af4a43a61 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -333,33 +333,20 @@ CHIP_ERROR ReadHandler::ProcessAttributePathList(AttributePathList::Parser & aAt AttributePath::Parser path; err = path.Init(reader); SuccessOrExit(err); - err = path.GetNodeId(&(clusterInfo.mNodeId)); - SuccessOrExit(err); - err = path.GetEndpointId(&(clusterInfo.mEndpointId)); - SuccessOrExit(err); - err = path.GetClusterId(&(clusterInfo.mClusterId)); - SuccessOrExit(err); - err = path.GetFieldId(&(clusterInfo.mFieldId)); - if (CHIP_NO_ERROR == err) - { - clusterInfo.mFlags.Set(ClusterInfo::Flags::kFieldIdValid); - } - else if (CHIP_END_OF_TLV == err) - { - err = CHIP_NO_ERROR; - } - SuccessOrExit(err); + // TODO(#8364): Support wildcard paths here, should check if we have got valid input when implementing wildcard read. + SuccessOrExit(err = path.GetNodeId(clusterInfo.mNodeId)); + SuccessOrExit(err = path.GetEndpointId(clusterInfo.mEndpointId)); + SuccessOrExit(err = path.GetClusterId(clusterInfo.mClusterId)); + SuccessOrExit(err = path.GetFieldId(clusterInfo.mFieldId)); + SuccessOrExit(err = path.GetListIndex(clusterInfo.mListIndex)); + + VerifyOrExit(!clusterInfo.mListIndex.HasValue() || clusterInfo.mFieldId.HasValue(), + err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH); + + // TODO(#8364): We assume the path is a concrete path here, will be removed when implementing the wildcard read. + VerifyOrExit(clusterInfo.mEndpointId.HasValue() && clusterInfo.mClusterId.HasValue() && clusterInfo.mFieldId.HasValue(), + err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH); - err = path.GetListIndex(&(clusterInfo.mListIndex)); - if (CHIP_NO_ERROR == err) - { - VerifyOrExit(clusterInfo.mFlags.Has(ClusterInfo::Flags::kFieldIdValid), err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH); - clusterInfo.mFlags.Set(ClusterInfo::Flags::kListIndexValid); - } - else if (CHIP_END_OF_TLV == err) - { - err = CHIP_NO_ERROR; - } SuccessOrExit(err); err = InteractionModelEngine::GetInstance()->PushFront(mpAttributeClusterInfoList, clusterInfo); SuccessOrExit(err); @@ -389,21 +376,15 @@ CHIP_ERROR ReadHandler::ProcessEventPathList(EventPathList::Parser & aEventPathL EventPath::Parser path; err = path.Init(reader); SuccessOrExit(err); - err = path.GetNodeId(&(clusterInfo.mNodeId)); - SuccessOrExit(err); - err = path.GetEndpointId(&(clusterInfo.mEndpointId)); - SuccessOrExit(err); - err = path.GetClusterId(&(clusterInfo.mClusterId)); - SuccessOrExit(err); - err = path.GetEventId(&(clusterInfo.mEventId)); - if (CHIP_NO_ERROR == err) - { - clusterInfo.mFlags.Set(ClusterInfo::Flags::kEventIdValid); - } - else if (CHIP_END_OF_TLV == err) - { - err = CHIP_NO_ERROR; - } + SuccessOrExit(err = path.GetNodeId(clusterInfo.mNodeId)); + SuccessOrExit(err = path.GetEndpointId(clusterInfo.mEndpointId)); + SuccessOrExit(err = path.GetClusterId(clusterInfo.mClusterId)); + SuccessOrExit(err = path.GetEventId(clusterInfo.mEventId)); + + // TODO: We assume the path is a concrete path here, will be removed when implementing the wildcard event read. + VerifyOrExit(clusterInfo.mEndpointId.HasValue() && clusterInfo.mClusterId.HasValue(), + err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH); + SuccessOrExit(err); err = InteractionModelEngine::GetInstance()->PushFront(mpEventClusterInfoList, clusterInfo); SuccessOrExit(err); diff --git a/src/app/WriteHandler.cpp b/src/app/WriteHandler.cpp index 7145f8c275536d..c2fe6b8be1ae9a 100644 --- a/src/app/WriteHandler.cpp +++ b/src/app/WriteHandler.cpp @@ -122,32 +122,17 @@ CHIP_ERROR WriteHandler::ProcessAttributeDataList(TLV::TLVReader & aAttributeDat err = element.GetAttributePath(&attributePath); SuccessOrExit(err); - err = attributePath.GetNodeId(&(clusterInfo.mNodeId)); - SuccessOrExit(err); - - err = attributePath.GetEndpointId(&(clusterInfo.mEndpointId)); - SuccessOrExit(err); - - err = attributePath.GetClusterId(&(clusterInfo.mClusterId)); - SuccessOrExit(err); - - err = attributePath.GetFieldId(&(clusterInfo.mFieldId)); - if (CHIP_NO_ERROR == err) - { - clusterInfo.mFlags.Set(ClusterInfo::Flags::kFieldIdValid); - } - else if (CHIP_END_OF_TLV == err) - { - err = CHIP_NO_ERROR; - } - SuccessOrExit(err); - - err = attributePath.GetListIndex(&(clusterInfo.mListIndex)); - if (CHIP_NO_ERROR == err) - { - VerifyOrExit(clusterInfo.mFlags.Has(ClusterInfo::Flags::kFieldIdValid), err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH); - clusterInfo.mFlags.Set(ClusterInfo::Flags::kListIndexValid); - } + SuccessOrExit(err = attributePath.GetNodeId(clusterInfo.mNodeId)); + SuccessOrExit(err = attributePath.GetEndpointId(clusterInfo.mEndpointId)); + SuccessOrExit(err = attributePath.GetClusterId(clusterInfo.mClusterId)); + SuccessOrExit(err = attributePath.GetFieldId(clusterInfo.mFieldId)); + SuccessOrExit(err = attributePath.GetListIndex(clusterInfo.mListIndex)); + + VerifyOrExit(!clusterInfo.mListIndex.HasValue() || clusterInfo.mFieldId.HasValue(), + err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH); + // Reject all wildcard writes. + VerifyOrExit(clusterInfo.mEndpointId.HasValue() && clusterInfo.mClusterId.HasValue() && clusterInfo.mFieldId.HasValue(), + err = CHIP_ERROR_IM_MALFORMED_ATTRIBUTE_PATH); err = element.GetData(&dataReader); SuccessOrExit(err); diff --git a/src/app/reporting/Engine.cpp b/src/app/reporting/Engine.cpp index f5562d151c72a1..b98c32e85386c9 100644 --- a/src/app/reporting/Engine.cpp +++ b/src/app/reporting/Engine.cpp @@ -65,19 +65,21 @@ CHIP_ERROR Engine::RetrieveClusterData(AttributeDataList::Builder & aAttributeDataList, ClusterInfo & aClusterInfo) { CHIP_ERROR err = CHIP_NO_ERROR; - ConcreteAttributePath path(aClusterInfo.mEndpointId, aClusterInfo.mClusterId, aClusterInfo.mFieldId); + + // TODO: We assume the path is a concrete path here, will be changed when implementing the wildcard event read. + ConcreteAttributePath path(aClusterInfo.mEndpointId.Value(), aClusterInfo.mClusterId.Value(), aClusterInfo.mFieldId.Value()); AttributeDataElement::Builder attributeDataElementBuilder = aAttributeDataList.CreateAttributeDataElementBuilder(); AttributePath::Builder attributePathBuilder = attributeDataElementBuilder.CreateAttributePathBuilder(); - attributePathBuilder.NodeId(aClusterInfo.mNodeId) - .EndpointId(aClusterInfo.mEndpointId) - .ClusterId(aClusterInfo.mClusterId) - .FieldId(aClusterInfo.mFieldId) + attributePathBuilder.NodeId(aClusterInfo.mNodeId.ValueOr(kUndefinedNodeId)) + .EndpointId(aClusterInfo.mEndpointId.Value()) + .ClusterId(aClusterInfo.mClusterId.Value()) + .FieldId(aClusterInfo.mFieldId.Value()) .EndOfAttributePath(); err = attributePathBuilder.GetError(); SuccessOrExit(err); - ChipLogDetail(DataManagement, " Cluster %" PRIx32 ", Field %" PRIx32 " is dirty", aClusterInfo.mClusterId, - aClusterInfo.mFieldId); + ChipLogDetail(DataManagement, " Cluster %" PRIx32 ", Field %" PRIx32 " is dirty", aClusterInfo.mClusterId.Value(), + aClusterInfo.mFieldId.Value()); err = ReadSingleClusterData(path, attributeDataElementBuilder.GetWriter(), nullptr /* data exists */); SuccessOrExit(err); @@ -89,7 +91,7 @@ Engine::RetrieveClusterData(AttributeDataList::Builder & aAttributeDataList, Clu if (err != CHIP_NO_ERROR) { ChipLogError(DataManagement, "Error retrieving data from clusterId: " ChipLogFormatMEI ", err = %" CHIP_ERROR_FORMAT, - ChipLogValueMEI(aClusterInfo.mClusterId), err.Format()); + ChipLogValueMEI(aClusterInfo.mClusterId.Value()), err.Format()); } return err; diff --git a/src/app/tests/TestClusterInfo.cpp b/src/app/tests/TestClusterInfo.cpp index 932fe062104e72..04824f955fd433 100644 --- a/src/app/tests/TestClusterInfo.cpp +++ b/src/app/tests/TestClusterInfo.cpp @@ -34,21 +34,17 @@ void TestAttributePathIncludedSameFieldId(nlTestSuite * apSuite, void * apContex ClusterInfo clusterInfo1; ClusterInfo clusterInfo2; ClusterInfo clusterInfo3; - clusterInfo1.mFlags.Set(ClusterInfo::Flags::kFieldIdValid); - clusterInfo2.mFlags.Set(ClusterInfo::Flags::kFieldIdValid); - clusterInfo3.mFlags.Set(ClusterInfo::Flags::kFieldIdValid); - clusterInfo1.mFieldId = 1; - clusterInfo2.mFieldId = 1; - clusterInfo3.mFieldId = 1; + clusterInfo1.mFieldId.SetValue(1); + clusterInfo2.mFieldId.SetValue(1); + clusterInfo3.mFieldId.SetValue(1); NL_TEST_ASSERT(apSuite, clusterInfo1.IsAttributePathSupersetOf(clusterInfo2)); - clusterInfo2.mFlags.Set(ClusterInfo::Flags::kListIndexValid); - clusterInfo2.mListIndex = 1; + clusterInfo2.mListIndex.SetValue(1); NL_TEST_ASSERT(apSuite, clusterInfo1.IsAttributePathSupersetOf(clusterInfo2)); - clusterInfo1.mFlags.Set(ClusterInfo::Flags::kListIndexValid); + clusterInfo1.mListIndex.SetValue(0); NL_TEST_ASSERT(apSuite, !clusterInfo1.IsAttributePathSupersetOf(clusterInfo3)); - clusterInfo3.mFlags.Set(ClusterInfo::Flags::kListIndexValid); + clusterInfo3.mListIndex.SetValue(0); NL_TEST_ASSERT(apSuite, clusterInfo1.IsAttributePathSupersetOf(clusterInfo3)); - clusterInfo3.mListIndex = 1; + clusterInfo3.mListIndex.SetValue(1); NL_TEST_ASSERT(apSuite, !clusterInfo1.IsAttributePathSupersetOf(clusterInfo3)); } @@ -56,19 +52,17 @@ void TestAttributePathIncludedDifferentFieldId(nlTestSuite * apSuite, void * apC { ClusterInfo clusterInfo1; ClusterInfo clusterInfo2; - clusterInfo1.mFlags.Set(ClusterInfo::Flags::kFieldIdValid); - clusterInfo2.mFlags.Set(ClusterInfo::Flags::kFieldIdValid); - clusterInfo1.mFieldId = 1; - clusterInfo2.mFieldId = 2; + clusterInfo1.mFieldId.SetValue(1); + clusterInfo2.mFieldId.SetValue(2); NL_TEST_ASSERT(apSuite, !clusterInfo1.IsAttributePathSupersetOf(clusterInfo2)); - clusterInfo1.mFieldId = 0xFFFFFFFF; - clusterInfo2.mFieldId = 2; + clusterInfo1.mFieldId.ClearValue(); + clusterInfo2.mFieldId.SetValue(2); NL_TEST_ASSERT(apSuite, clusterInfo1.IsAttributePathSupersetOf(clusterInfo2)); - clusterInfo1.mFieldId = 0xFFFFFFFF; - clusterInfo2.mFieldId = 0xFFFFFFFF; + clusterInfo1.mFieldId.ClearValue(); + clusterInfo2.mFieldId.ClearValue(); NL_TEST_ASSERT(apSuite, clusterInfo1.IsAttributePathSupersetOf(clusterInfo2)); - clusterInfo1.mFieldId = 1; - clusterInfo2.mFieldId = 0xFFFFFFFF; + clusterInfo1.mFieldId.SetValue(1); + clusterInfo2.mFieldId.ClearValue(); NL_TEST_ASSERT(apSuite, !clusterInfo1.IsAttributePathSupersetOf(clusterInfo2)); } @@ -76,8 +70,8 @@ void TestAttributePathIncludedDifferentEndpointId(nlTestSuite * apSuite, void * { ClusterInfo clusterInfo1; ClusterInfo clusterInfo2; - clusterInfo1.mEndpointId = 1; - clusterInfo2.mEndpointId = 2; + clusterInfo1.mEndpointId.SetValue(1); + clusterInfo2.mEndpointId.SetValue(2); NL_TEST_ASSERT(apSuite, !clusterInfo1.IsAttributePathSupersetOf(clusterInfo2)); } @@ -85,8 +79,8 @@ void TestAttributePathIncludedDifferentClusterId(nlTestSuite * apSuite, void * a { ClusterInfo clusterInfo1; ClusterInfo clusterInfo2; - clusterInfo1.mClusterId = 1; - clusterInfo2.mClusterId = 2; + clusterInfo1.mClusterId.SetValue(1); + clusterInfo2.mClusterId.SetValue(2); NL_TEST_ASSERT(apSuite, !clusterInfo1.IsAttributePathSupersetOf(clusterInfo2)); } } // namespace TestClusterInfo diff --git a/src/app/tests/TestEventLogging.cpp b/src/app/tests/TestEventLogging.cpp index 0511a183f56d1b..4ac802c26cb2d9 100644 --- a/src/app/tests/TestEventLogging.cpp +++ b/src/app/tests/TestEventLogging.cpp @@ -219,15 +219,15 @@ static void CheckLogEventWithEvictToNextBuffer(nlTestSuite * apSuite, void * apC NL_TEST_ASSERT(apSuite, (eid5 + 1) == eid6); chip::app::ClusterInfo testClusterInfo1; - testClusterInfo1.mNodeId = kTestDeviceNodeId1; - testClusterInfo1.mEndpointId = kTestEndpointId; - testClusterInfo1.mClusterId = kLivenessClusterId; - testClusterInfo1.mEventId = kLivenessChangeEvent; + testClusterInfo1.mNodeId.SetValue(kTestDeviceNodeId1); + testClusterInfo1.mEndpointId.SetValue(kTestEndpointId); + testClusterInfo1.mClusterId.SetValue(kLivenessClusterId); + testClusterInfo1.mEventId.SetValue(kLivenessChangeEvent); chip::app::ClusterInfo testClusterInfo2; - testClusterInfo2.mNodeId = kTestDeviceNodeId2; - testClusterInfo2.mEndpointId = kTestEndpointId; - testClusterInfo2.mClusterId = kLivenessClusterId; - testClusterInfo2.mEventId = kLivenessChangeEvent; + testClusterInfo2.mNodeId.SetValue(kTestDeviceNodeId2); + testClusterInfo2.mEndpointId.SetValue(kTestEndpointId); + testClusterInfo2.mClusterId.SetValue(kLivenessClusterId); + testClusterInfo2.mEventId.SetValue(kLivenessChangeEvent); CheckLogReadOut(apSuite, logMgmt, chip::app::PriorityLevel::Info, eid1, 3, &testClusterInfo1); CheckLogReadOut(apSuite, logMgmt, chip::app::PriorityLevel::Info, eid2, 2, &testClusterInfo1); diff --git a/src/app/tests/TestInteractionModelEngine.cpp b/src/app/tests/TestInteractionModelEngine.cpp index b55200f7a901f7..33850bc9c5884e 100644 --- a/src/app/tests/TestInteractionModelEngine.cpp +++ b/src/app/tests/TestInteractionModelEngine.cpp @@ -71,9 +71,9 @@ void TestInteractionModelEngine::TestClusterInfoPushRelease(nlTestSuite * apSuit ClusterInfo clusterInfo2; ClusterInfo clusterInfo3; - clusterInfo1.mEndpointId = 1; - clusterInfo2.mEndpointId = 2; - clusterInfo3.mEndpointId = 3; + clusterInfo1.mEndpointId.SetValue(1); + clusterInfo2.mEndpointId.SetValue(2); + clusterInfo3.mEndpointId.SetValue(3); InteractionModelEngine::GetInstance()->PushFront(clusterInfoList, clusterInfo1); NL_TEST_ASSERT(apSuite, clusterInfoList != nullptr && clusterInfo1.mEndpointId == clusterInfoList->mEndpointId); @@ -99,21 +99,17 @@ void TestInteractionModelEngine::TestMergeOverlappedAttributePath(nlTestSuite * NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ClusterInfo clusterInfoList[2]; - clusterInfoList[0].mFlags.Set(chip::app::ClusterInfo::Flags::kFieldIdValid); - clusterInfoList[0].mFieldId = 1; - clusterInfoList[1].mFlags.Set(chip::app::ClusterInfo::Flags::kFieldIdValid); - clusterInfoList[1].mFieldId = 2; + clusterInfoList[0].mFieldId.SetValue(1); + clusterInfoList[1].mFieldId.SetValue(2); chip::app::ClusterInfo testClusterInfo; - testClusterInfo.mFlags.Set(chip::app::ClusterInfo::Flags::kFieldIdValid); - testClusterInfo.mFieldId = 3; + testClusterInfo.mFieldId.SetValue(3); NL_TEST_ASSERT(apSuite, !InteractionModelEngine::GetInstance()->MergeOverlappedAttributePath(clusterInfoList, testClusterInfo)); - testClusterInfo.mFieldId = 0xFFFFFFFF; + testClusterInfo.mFieldId.ClearValue(); NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->MergeOverlappedAttributePath(clusterInfoList, testClusterInfo)); - testClusterInfo.mFlags.Set(chip::app::ClusterInfo::Flags::kListIndexValid); - testClusterInfo.mFieldId = 1; - testClusterInfo.mListIndex = 2; + testClusterInfo.mFieldId.SetValue(1); + testClusterInfo.mListIndex.SetValue(2); NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->MergeOverlappedAttributePath(clusterInfoList, testClusterInfo)); } } // namespace app diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp index 7f7352560b7b54..216bd02b4bc84d 100644 --- a/src/app/tests/TestReadInteraction.cpp +++ b/src/app/tests/TestReadInteraction.cpp @@ -921,36 +921,30 @@ void TestReadInteraction::TestSubscribeRoundtrip(nlTestSuite * apSuite, void * a NL_TEST_ASSERT(apSuite, delegate.mNumSubscriptions == 1); chip::app::ClusterInfo dirtyPath1; - dirtyPath1.mClusterId = kTestClusterId; - dirtyPath1.mEndpointId = kTestEndpointId; - dirtyPath1.mFlags.Set(chip::app::ClusterInfo::Flags::kFieldIdValid); - dirtyPath1.mFieldId = 1; + dirtyPath1.mClusterId.SetValue(kTestClusterId); + dirtyPath1.mEndpointId.SetValue(kTestEndpointId); + dirtyPath1.mFieldId.SetValue(1); chip::app::ClusterInfo dirtyPath2; - dirtyPath2.mClusterId = kTestClusterId; - dirtyPath2.mEndpointId = kTestEndpointId; - dirtyPath2.mFlags.Set(chip::app::ClusterInfo::Flags::kFieldIdValid); - dirtyPath2.mFieldId = 2; + dirtyPath2.mClusterId.SetValue(kTestClusterId); + dirtyPath2.mEndpointId.SetValue(kTestEndpointId); + dirtyPath2.mFieldId.SetValue(2); chip::app::ClusterInfo dirtyPath3; - dirtyPath2.mClusterId = kTestClusterId; - dirtyPath2.mEndpointId = kTestEndpointId; - dirtyPath2.mFlags.Set(chip::app::ClusterInfo::Flags::kFieldIdValid); - dirtyPath2.mFlags.Set(chip::app::ClusterInfo::Flags::kListIndexValid); - dirtyPath2.mFieldId = 2; - dirtyPath2.mListIndex = 1; + dirtyPath3.mClusterId.SetValue(kTestClusterId); + dirtyPath3.mEndpointId.SetValue(kTestEndpointId); + dirtyPath3.mFieldId.SetValue(2); + dirtyPath3.mListIndex.SetValue(1); chip::app::ClusterInfo dirtyPath4; - dirtyPath4.mClusterId = kTestClusterId; - dirtyPath4.mEndpointId = kTestEndpointId; - dirtyPath4.mFlags.Set(chip::app::ClusterInfo::Flags::kFieldIdValid); - dirtyPath4.mFieldId = 3; + dirtyPath4.mClusterId.SetValue(kTestClusterId); + dirtyPath4.mEndpointId.SetValue(kTestEndpointId); + dirtyPath4.mFieldId.SetValue(3); chip::app::ClusterInfo dirtyPath5; - dirtyPath5.mClusterId = kTestClusterId; - dirtyPath5.mEndpointId = kTestEndpointId; - dirtyPath5.mFlags.Set(chip::app::ClusterInfo::Flags::kFieldIdValid); - dirtyPath5.mFieldId = 4; + dirtyPath5.mClusterId.SetValue(kTestClusterId); + dirtyPath5.mEndpointId.SetValue(kTestEndpointId); + dirtyPath5.mFieldId.SetValue(4); // Test report with 2 different path delegate.mpReadHandler->mHoldReport = false; diff --git a/src/app/tests/TestWriteInteraction.cpp b/src/app/tests/TestWriteInteraction.cpp index 46df2f2a0b09d9..532395aa85d1d1 100644 --- a/src/app/tests/TestWriteInteraction.cpp +++ b/src/app/tests/TestWriteInteraction.cpp @@ -269,8 +269,9 @@ CHIP_ERROR WriteSingleClusterData(ClusterInfo & aClusterInfo, TLV::TLVReader & a writer.Init(attributeDataTLV); writer.CopyElement(TLV::AnonymousTag, aReader); attributeDataTLVLen = writer.GetLengthWritten(); - return aWriteHandler->AddStatus(AttributePathParams(aClusterInfo.mEndpointId, aClusterInfo.mClusterId, aClusterInfo.mFieldId), - Protocols::InteractionModel::Status::Success); + return aWriteHandler->AddStatus( + AttributePathParams(aClusterInfo.mEndpointId.Value(), aClusterInfo.mClusterId.Value(), aClusterInfo.mFieldId.Value()), + Protocols::InteractionModel::Status::Success); } void TestWriteInteraction::TestWriteRoundtripWithClusterObjects(nlTestSuite * apSuite, void * apContext) diff --git a/src/app/tests/integration/chip_im_initiator.cpp b/src/app/tests/integration/chip_im_initiator.cpp index bd1d7ce307e4ac..a889b67d827376 100644 --- a/src/app/tests/integration/chip_im_initiator.cpp +++ b/src/app/tests/integration/chip_im_initiator.cpp @@ -647,7 +647,8 @@ CHIP_ERROR ReadSingleClusterData(const ConcreteAttributePath & aPath, TLV::TLVWr CHIP_ERROR WriteSingleClusterData(ClusterInfo & aClusterInfo, TLV::TLVReader & aReader, WriteHandler *) { - if (aClusterInfo.mClusterId != kTestClusterId || aClusterInfo.mEndpointId != kTestEndpointId) + if (!(aClusterInfo.mClusterId == Optional(kTestClusterId)) || + !(aClusterInfo.mEndpointId == Optional(kTestEndpointId))) { return CHIP_ERROR_INVALID_ARGUMENT; } diff --git a/src/app/tests/integration/chip_im_responder.cpp b/src/app/tests/integration/chip_im_responder.cpp index 0aa92dc531fd40..b9df0e0a08c25b 100644 --- a/src/app/tests/integration/chip_im_responder.cpp +++ b/src/app/tests/integration/chip_im_responder.cpp @@ -168,14 +168,13 @@ void InitializeEventLogging(chip::Messaging::ExchangeManager * apMgr) void MutateClusterHandler(chip::System::Layer * systemLayer, void * appState) { chip::app::ClusterInfo dirtyPath; - dirtyPath.mClusterId = kTestClusterId; - dirtyPath.mEndpointId = kTestEndpointId; - dirtyPath.mFlags.Set(chip::app::ClusterInfo::Flags::kFieldIdValid); + dirtyPath.mClusterId.SetValue(kTestClusterId); + dirtyPath.mEndpointId.SetValue(kTestEndpointId); printf("MutateClusterHandler is triggered..."); // send dirty change if (!testSyncReport) { - dirtyPath.mFieldId = 1; + dirtyPath.mFieldId.SetValue(1); chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(dirtyPath); chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun(); chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(1), MutateClusterHandler, NULL); @@ -183,7 +182,7 @@ void MutateClusterHandler(chip::System::Layer * systemLayer, void * appState) } else { - dirtyPath.mFieldId = 10; // unknown field + dirtyPath.mFieldId.SetValue(10); // unknown field chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(dirtyPath); // send sync message(empty report) chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun(); diff --git a/src/app/util/ember-compatibility-functions.cpp b/src/app/util/ember-compatibility-functions.cpp index 3d7d6e4d368cd4..4a7d67c10d1465 100644 --- a/src/app/util/ember-compatibility-functions.cpp +++ b/src/app/util/ember-compatibility-functions.cpp @@ -443,7 +443,7 @@ static Protocols::InteractionModel::Status WriteSingleClusterDataInternal(Cluste // Passing nullptr as buf to emberAfReadAttribute means we only need attribute type here, and ember will not do data read & // copy in this case. const EmberAfAttributeMetadata * attributeMetadata = emberAfLocateAttributeMetadata( - aClusterInfo.mEndpointId, aClusterInfo.mClusterId, aClusterInfo.mFieldId, CLUSTER_MASK_SERVER, 0); + aClusterInfo.mEndpointId.Value(), aClusterInfo.mClusterId.Value(), aClusterInfo.mFieldId.Value(), CLUSTER_MASK_SERVER, 0); if (attributeMetadata == nullptr) { @@ -464,18 +464,18 @@ static Protocols::InteractionModel::Status WriteSingleClusterDataInternal(Cluste return Protocols::InteractionModel::Status::InvalidValue; } - return ToInteractionModelStatus(emberAfWriteAttributeExternal(aClusterInfo.mEndpointId, aClusterInfo.mClusterId, - aClusterInfo.mFieldId, CLUSTER_MASK_SERVER, 0, attributeData, - attributeMetadata->attributeType)); + return ToInteractionModelStatus(emberAfWriteAttributeExternal(aClusterInfo.mEndpointId.Value(), aClusterInfo.mClusterId.Value(), + aClusterInfo.mFieldId.Value(), CLUSTER_MASK_SERVER, 0, + attributeData, attributeMetadata->attributeType)); } CHIP_ERROR WriteSingleClusterData(ClusterInfo & aClusterInfo, TLV::TLVReader & aReader, WriteHandler * apWriteHandler) { AttributePathParams attributePathParams; - attributePathParams.mNodeId = aClusterInfo.mNodeId; - attributePathParams.mEndpointId = aClusterInfo.mEndpointId; - attributePathParams.mClusterId = aClusterInfo.mClusterId; - attributePathParams.mFieldId = aClusterInfo.mFieldId; + attributePathParams.mNodeId = aClusterInfo.mNodeId.Value(); + attributePathParams.mEndpointId = aClusterInfo.mEndpointId.Value(); + attributePathParams.mClusterId = aClusterInfo.mClusterId.Value(); + attributePathParams.mFieldId = aClusterInfo.mFieldId.Value(); attributePathParams.mFlags.Set(AttributePathParams::Flags::kFieldIdValid); auto imCode = WriteSingleClusterDataInternal(aClusterInfo, aReader, apWriteHandler); @@ -498,10 +498,9 @@ void MatterReportingAttributeChangeCallback(EndpointId endpoint, ClusterId clust void MatterReportingAttributeChangeCallback(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId) { ClusterInfo info; - info.mClusterId = clusterId; - info.mFieldId = attributeId; - info.mEndpointId = endpoint; - info.mFlags.Set(ClusterInfo::Flags::kFieldIdValid); + info.mClusterId.SetValue(clusterId); + info.mFieldId.SetValue(attributeId); + info.mEndpointId.SetValue(endpoint); InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(info); } diff --git a/src/app/util/im-client-callbacks.cpp b/src/app/util/im-client-callbacks.cpp index c60631f496907c..e24520d1c3e548 100644 --- a/src/app/util/im-client-callbacks.cpp +++ b/src/app/util/im-client-callbacks.cpp @@ -366,7 +366,10 @@ bool IMReadReportAttributesResponseCallback(const app::ReadClient * apReadClient TLV::TLVReader * apData, Protocols::InteractionModel::Status status) { ChipLogProgress(Zcl, "ReadAttributesResponse:"); - ChipLogProgress(Zcl, " ClusterId: " ChipLogFormatMEI, ChipLogValueMEI(aPath.mClusterId)); + if (aPath.mClusterId.HasValue()) + { + ChipLogProgress(Zcl, " ClusterId: " ChipLogFormatMEI, ChipLogValueMEI(aPath.mClusterId.Value())); + } Callback::Cancelable * onSuccessCallback = nullptr; Callback::Cancelable * onFailureCallback = nullptr; @@ -378,8 +381,12 @@ bool IMReadReportAttributesResponseCallback(const app::ReadClient * apReadClient CHIP_ERROR err = CHIP_NO_ERROR; if (apReadClient->IsSubscriptionType()) { - err = gCallbacks.GetReportCallback(sourceId, aPath.mEndpointId, aPath.mClusterId, aPath.mFieldId, &onSuccessCallback, - &tlvFilter); + if (status == Protocols::InteractionModel::Status::Success) + { + // If the status is success, the path must be a concrete path. + err = gCallbacks.GetReportCallback(sourceId, aPath.mEndpointId.Value(), aPath.mClusterId.Value(), + aPath.mFieldId.Value(), &onSuccessCallback, &tlvFilter); + } } else { @@ -405,7 +412,10 @@ bool IMReadReportAttributesResponseCallback(const app::ReadClient * apReadClient return true; } - ChipLogProgress(Zcl, " attributeId: " ChipLogFormatMEI, ChipLogValueMEI(aPath.mFieldId)); + if (aPath.mFieldId.HasValue()) + { + ChipLogProgress(Zcl, " attributeId: " ChipLogFormatMEI, ChipLogValueMEI(aPath.mFieldId.Value())); + } LogIMStatus(status); if (status == Protocols::InteractionModel::Status::Success && apData != nullptr) diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 68ae2034805474..005c04e0978736 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -1651,7 +1651,7 @@ void DeviceControllerInteractionModelDelegate::OnReportData(const app::ReadClien CHIP_ERROR DeviceControllerInteractionModelDelegate::ReadError(app::ReadClient * apReadClient, CHIP_ERROR aError) { app::ClusterInfo path; - path.mNodeId = apReadClient->GetPeerNodeId(); + path.mNodeId.SetValue(apReadClient->GetPeerNodeId()); IMReadReportAttributesResponseCallback(apReadClient, path, nullptr, Protocols::InteractionModel::Status::Failure); return CHIP_NO_ERROR; } diff --git a/src/controller/TypedReadCallback.h b/src/controller/TypedReadCallback.h index 3464374f4ae700..2b7720739b9ed3 100644 --- a/src/controller/TypedReadCallback.h +++ b/src/controller/TypedReadCallback.h @@ -54,11 +54,12 @@ class TypedReadCallback final : public app::InteractionModelDelegate Protocols::InteractionModel::Status status) override { CHIP_ERROR err = CHIP_NO_ERROR; - app::ConcreteAttributePath attributePath = { aPath.mEndpointId, aPath.mClusterId, aPath.mFieldId }; + app::ConcreteAttributePath attributePath = { aPath.mEndpointId.Value(), aPath.mClusterId.Value(), aPath.mFieldId.Value() }; typename AttributeTypeInfo::DecodableType value; VerifyOrExit(status == Protocols::InteractionModel::Status::Success, err = CHIP_ERROR_IM_STATUS_CODE_RECEIVED); - VerifyOrExit(aPath.mClusterId == AttributeTypeInfo::GetClusterId() && aPath.mFieldId == AttributeTypeInfo::GetAttributeId(), + VerifyOrExit(aPath.mClusterId == Optional(AttributeTypeInfo::GetClusterId()) && + aPath.mFieldId == Optional(AttributeTypeInfo::GetAttributeId()), CHIP_ERROR_SCHEMA_MISMATCH); VerifyOrExit(apData != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); diff --git a/src/controller/python/chip/interaction_model/Delegate.cpp b/src/controller/python/chip/interaction_model/Delegate.cpp index 6d127373b597bb..912c203df04a6b 100644 --- a/src/controller/python/chip/interaction_model/Delegate.cpp +++ b/src/controller/python/chip/interaction_model/Delegate.cpp @@ -102,7 +102,9 @@ void PythonInteractionModelDelegate::OnReportData(const app::ReadClient * apRead } if (CHIP_NO_ERROR == err) { - AttributePath path{ .endpointId = aPath.mEndpointId, .clusterId = aPath.mClusterId, .fieldId = aPath.mFieldId }; + AttributePath path{ .endpointId = aPath.mEndpointId.Value(), + .clusterId = aPath.mClusterId.Value(), + .fieldId = aPath.mFieldId.Value() }; onReportDataFunct(apReadClient->GetPeerNodeId(), apReadClient->GetAppIdentifier(), /* TODO: Use real SubscriptionId */ apReadClient->IsSubscriptionType() ? 1 : 0, &path, sizeof(path), writerBuffer, writer.GetLengthWritten(), to_underlying(status)); diff --git a/src/controller/tests/data_model/TestWrite.cpp b/src/controller/tests/data_model/TestWrite.cpp index 4498f096116035..cbf8e5528b7007 100644 --- a/src/controller/tests/data_model/TestWrite.cpp +++ b/src/controller/tests/data_model/TestWrite.cpp @@ -66,8 +66,8 @@ CHIP_ERROR ReadSingleClusterData(const ConcreteAttributePath & aPath, TLV::TLVWr CHIP_ERROR WriteSingleClusterData(ClusterInfo & aClusterInfo, TLV::TLVReader & aReader, WriteHandler * aWriteHandler) { - if (aClusterInfo.mClusterId == TestCluster::Id && - aClusterInfo.mFieldId == TestCluster::Attributes::ListStructOctetString::TypeInfo::GetAttributeId()) + if (aClusterInfo.mClusterId == Optional(TestCluster::Id) && + aClusterInfo.mFieldId == Optional(TestCluster::Attributes::ListStructOctetString::TypeInfo::GetAttributeId())) { if (responseDirective == kSendAttributeSuccess) { @@ -87,12 +87,14 @@ CHIP_ERROR WriteSingleClusterData(ClusterInfo & aClusterInfo, TLV::TLVReader & a VerifyOrReturnError(i == 4, CHIP_ERROR_INVALID_ARGUMENT); - AttributePathParams attributePathParams(aClusterInfo.mClusterId, aClusterInfo.mEndpointId, aClusterInfo.mFieldId); + AttributePathParams attributePathParams(aClusterInfo.mClusterId.Value(), aClusterInfo.mEndpointId.Value(), + aClusterInfo.mFieldId.Value()); aWriteHandler->AddStatus(attributePathParams, Protocols::InteractionModel::Status::Success); } else { - AttributePathParams attributePathParams(aClusterInfo.mClusterId, aClusterInfo.mEndpointId, aClusterInfo.mFieldId); + AttributePathParams attributePathParams(aClusterInfo.mClusterId.Value(), aClusterInfo.mEndpointId.Value(), + aClusterInfo.mFieldId.Value()); aWriteHandler->AddStatus(attributePathParams, Protocols::InteractionModel::Status::Failure); } From d985744c22a037f5994221531adf0bc57d2c6da4 Mon Sep 17 00:00:00 2001 From: Song Guo Date: Tue, 26 Oct 2021 10:19:26 +0800 Subject: [PATCH 2/2] [IM] Implementing PathIterator for iterating attribute paths with wildcard fields --- examples/platform/linux/AppMain.cpp | 2 + src/app/BUILD.gn | 2 + src/app/ConcreteAttributePath.h | 11 +- src/app/PathIterator.cpp | 189 ++++++++++++++++ src/app/PathIterator.h | 93 ++++++++ src/app/tests/BUILD.gn | 2 + src/app/tests/TestPathIterator.cpp | 279 ++++++++++++++++++++++++ src/app/util/attribute-storage.cpp | 48 ++++ src/app/util/attribute-storage.h | 25 ++- src/app/util/mock/BUILD.gn | 27 +++ src/app/util/mock/Constants.h | 45 ++++ src/app/util/mock/attribute-storage.cpp | 186 ++++++++++++++++ 12 files changed, 904 insertions(+), 5 deletions(-) create mode 100644 src/app/PathIterator.cpp create mode 100644 src/app/PathIterator.h create mode 100644 src/app/tests/TestPathIterator.cpp create mode 100644 src/app/util/mock/BUILD.gn create mode 100644 src/app/util/mock/Constants.h create mode 100644 src/app/util/mock/attribute-storage.cpp diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp index b3583fd6b7c57c..9ee6ce6ad8ad14 100644 --- a/examples/platform/linux/AppMain.cpp +++ b/examples/platform/linux/AppMain.cpp @@ -56,6 +56,8 @@ #include "Options.h" +#include + using namespace chip; using namespace chip::Credentials; using namespace chip::DeviceLayer; diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 5fa15d41afd031..61f1d1834a0dfa 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -93,6 +93,8 @@ static_library("app") { "MessageDef/TimedRequest.cpp", "MessageDef/WriteRequest.cpp", "MessageDef/WriteResponse.cpp", + "PathIterator.cpp", + "PathIterator.h", "ReadClient.cpp", "ReadHandler.cpp", "WriteClient.cpp", diff --git a/src/app/ConcreteAttributePath.h b/src/app/ConcreteAttributePath.h index b3b44a8a2f359c..ac04da37c1283f 100644 --- a/src/app/ConcreteAttributePath.h +++ b/src/app/ConcreteAttributePath.h @@ -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) {} @@ -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 diff --git a/src/app/PathIterator.cpp b/src/app/PathIterator.cpp new file mode 100644 index 00000000000000..df181335178be3 --- /dev/null +++ b/src/app/PathIterator.cpp @@ -0,0 +1,189 @@ +/* + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace chip; + +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 chip::ClusterId emberAfGetNthClusterId(chip::EndpointId endpoint, uint8_t n, bool server); +extern chip::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 PathIterator::Reset(ClusterInfo * aClusterInfo) +{ + mpClusterInfo = aClusterInfo; + + // Reset iterator state + mEndpointIndex = UINT16_MAX; + mClusterIndex = UINT8_MAX; + mAttributeIndex = UINT16_MAX; + + Proceed(); +} + +void PathIterator::PrepareEndpointIndexRange(const Optional & aEndpointId, uint16_t * aBeginEndpointIndex, + uint16_t * aEndEndpointIndex) +{ + if (!aEndpointId.HasValue()) + { + *aBeginEndpointIndex = 0; + *aEndEndpointIndex = emberAfEndpointCount(); + } + else + { + *aBeginEndpointIndex = emberAfIndexFromEndpoint(aEndpointId.Value()); + *aEndEndpointIndex = *aBeginEndpointIndex + 1; + } +} + +void PathIterator::PrepareClusterIndexRange(EndpointId aEndpointId, const Optional & aClusterId, + uint8_t * aBeginClusterIndex, uint8_t * aEndClusterIndex) +{ + if (!aClusterId.HasValue()) + { + *aBeginClusterIndex = 0; + *aEndClusterIndex = emberAfClusterCount(aEndpointId, true /* server */); + } + else + { + *aBeginClusterIndex = emberAfClusterIndex(aEndpointId, aClusterId.Value(), CLUSTER_MASK_SERVER); + // If the given cluster id does not exists on the given endpoint, it will return uint8(0xFF), then endClusterIndex + // will be 0 + *aEndClusterIndex = *aBeginClusterIndex + 1; + } +} + +void PathIterator::PrepareAttributeIndexRange(EndpointId aEndpointId, ClusterId aClusterId, + const Optional & aAttributeId, uint16_t * aBeginAttributeIndex, + uint16_t * aEndAttributeIndex) +{ + if (!aAttributeId.HasValue()) + { + *aBeginAttributeIndex = 0; + *aEndAttributeIndex = emberAfGetServerAttributeCount(aEndpointId, aClusterId); + } + else + { + *aBeginAttributeIndex = emberAfGetServerAttributeIndexByAttributeId(aEndpointId, aClusterId, aAttributeId.Value()); + *aEndAttributeIndex = *aBeginAttributeIndex + 1; + } +} + +bool PathIterator::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.Value(); + mOutputPath.mClusterId = mpClusterInfo->mClusterId.Value(); + mOutputPath.mAttributeId = mpClusterInfo->mFieldId.Value(); + + // Prepare for next iteration + (mpClusterInfo = mpClusterInfo->mpNext, mEndpointIndex = UINT16_MAX); + return true; + } + uint16_t beginEndpointIndex, endEndpointIndex; + PrepareEndpointIndexRange(mpClusterInfo->mEndpointId, &beginEndpointIndex, &endEndpointIndex); + + if (mEndpointIndex == UINT16_MAX) + { + // If we have not started iterating over the endpoints yet. + mEndpointIndex = beginEndpointIndex; + mClusterIndex = UINT8_MAX; + } + + for (; mEndpointIndex < endEndpointIndex; (mEndpointIndex++, mClusterIndex = UINT8_MAX, mAttributeIndex = UINT16_MAX)) + { + EndpointId endpointId = emberAfEndpointFromIndex(mEndpointIndex); + uint8_t beginClusterIndex, endClusterIndex; + PrepareClusterIndexRange(endpointId, mpClusterInfo->mClusterId, &beginClusterIndex, &endClusterIndex); + + if (mClusterIndex == UINT8_MAX) + { + // If we have not started iterating over the clusters yet. + mClusterIndex = beginClusterIndex; + mAttributeIndex = UINT16_MAX; + } + + for (; mClusterIndex < endClusterIndex; (mClusterIndex++, mAttributeIndex = UINT16_MAX)) + { + ClusterId clusterId = emberAfGetNthClusterId(endpointId, mClusterIndex, true /* server */); + uint16_t beginAttributeIndex, endAttributeIndex; + PrepareAttributeIndexRange(endpointId, clusterId, mpClusterInfo->mFieldId, &beginAttributeIndex, + &endAttributeIndex); + if (mAttributeIndex == UINT16_MAX) + { + // If we have not started iterating over the attributes yet. + mAttributeIndex = beginAttributeIndex; + } + + if (mAttributeIndex < endAttributeIndex) + { + mOutputPath.mAttributeId = emberAfGetServerAttributeIdByIndex(endpointId, clusterId, mAttributeIndex); + 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 diff --git a/src/app/PathIterator.h b/src/app/PathIterator.h new file mode 100644 index 00000000000000..51ddcbbef23e8b --- /dev/null +++ b/src/app/PathIterator.h @@ -0,0 +1,93 @@ +/* + * + * 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 + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { + +/** + * PathIterator is used for iterate over a chain of cluster info. + * The PathIterator is copiable, however, the given cluster info must be valid when calling proceed. + * + * PathIterator will expand attribute paths with wildcards. + * + * The typical use of PathIterator may look like: + * ConcreteAttributePath path; + * for (PathIterator iterator(clusterInfo); iterator.Get(path); iterator.Proceed()) {...} + */ +class PathIterator +{ +public: + PathIterator(ClusterInfo * aClusterInfo) { Reset(aClusterInfo); } + // Init initialized / resets the state of the path iterator. + void Reset(ClusterInfo * aClusterInfo); + /** + * Proceed the iterator to the next attribute path in the given cluster info. + */ + 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; + uint8_t mClusterIndex; + uint16_t mAttributeIndex; + + ConcreteAttributePath mOutputPath = ConcreteAttributePath(); + + void PrepareEndpointIndexRange(const Optional & aEndpointId, uint16_t * aBeginEndpointIndex, + uint16_t * aEndEndpointIndex); + void PrepareClusterIndexRange(EndpointId aEndpointId, const Optional & aClusterId, uint8_t * aBeginClusterIndex, + uint8_t * aEndClusterIndex); + void PrepareAttributeIndexRange(EndpointId aEndpointId, ClusterId aClusterId, const Optional & aAttributeId, + uint16_t * aBeginAttributeIndex, uint16_t * aEndAttributeIndex); +}; +} // namespace app +} // namespace chip diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 9bfd53e0622530..a812d75cff0931 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -50,6 +50,7 @@ chip_test_suite("tests") { "TestEventPathParams.cpp", "TestInteractionModelEngine.cpp", "TestMessageDef.cpp", + "TestPathIterator.cpp", "TestStatusResponse.cpp", ] @@ -69,6 +70,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", ] diff --git a/src/app/tests/TestPathIterator.cpp b/src/app/tests/TestPathIterator.cpp new file mode 100644 index 00000000000000..e27463689dc06c --- /dev/null +++ b/src/app/tests/TestPathIterator.cpp @@ -0,0 +1,279 @@ +/* + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace chip; + +namespace { + +using P = app::ConcreteAttributePath; + +void TestAllWildcard(nlTestSuite * apSuite, void * apContext) +{ + app::ClusterInfo clusInfo; + + app::ConcreteAttributePath path; + P paths[] = { + P(0xFFFE, 0xFFF1'0001, 0x0000'FFFD), P(0xFFFE, 0xFFF1'0001, 0x0000'FFFC), P(0xFFFE, 0xFFF1'0002, 0x0000'FFFD), + P(0xFFFE, 0xFFF1'0002, 0x0000'FFFC), P(0xFFFE, 0xFFF1'0002, 0xFFF1'0001), P(0xFFFD, 0xFFF1'0001, 0x0000'FFFD), + P(0xFFFD, 0xFFF1'0001, 0x0000'FFFC), P(0xFFFD, 0xFFF1'0002, 0x0000'FFFD), P(0xFFFD, 0xFFF1'0002, 0x0000'FFFC), + P(0xFFFD, 0xFFF1'0002, 0xFFF1'0001), P(0xFFFD, 0xFFF1'0002, 0xFFF1'0002), P(0xFFFD, 0xFFF1'0003, 0x0000'FFFD), + P(0xFFFD, 0xFFF1'0003, 0x0000'FFFC), P(0xFFFD, 0xFFF1'0003, 0xFFF1'0001), P(0xFFFD, 0xFFF1'0003, 0xFFF1'0002), + P(0xFFFD, 0xFFF1'0003, 0xFFF1'0003), P(0xFFFC, 0xFFF1'0001, 0x0000'FFFD), P(0xFFFC, 0xFFF1'0001, 0x0000'FFFC), + P(0xFFFC, 0xFFF1'0001, 0xFFF1'0001), P(0xFFFC, 0xFFF1'0002, 0x0000'FFFD), P(0xFFFC, 0xFFF1'0002, 0x0000'FFFC), + P(0xFFFC, 0xFFF1'0002, 0xFFF1'0001), P(0xFFFC, 0xFFF1'0002, 0xFFF1'0002), P(0xFFFC, 0xFFF1'0002, 0xFFF1'0003), + P(0xFFFC, 0xFFF1'0002, 0xFFF1'0004), P(0xFFFC, 0xFFF1'0003, 0x0000'FFFD), P(0xFFFC, 0xFFF1'0003, 0x0000'FFFC), + P(0xFFFC, 0xFFF1'0004, 0x0000'FFFD), P(0xFFFC, 0xFFF1'0004, 0x0000'FFFC), + }; + + size_t index = 0; + + for (app::PathIterator iter(&clusInfo); iter.Get(path); iter.Proceed()) + { + ChipLogDetail(AppServer, "Visited Attribute: 0x%04" PRIX16 " / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, + ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); + // NL_TEST_ASSERT(apSuite, index < ArraySize(paths) && paths[index] == path); + index++; + } + NL_TEST_ASSERT(apSuite, index == ArraySize(paths)); +} + +void TestWildcardEndpoint(nlTestSuite * apSuite, void * apContext) +{ + app::ClusterInfo clusInfo; + clusInfo.mClusterId.SetValue(Test::MockClusterId(3)); + clusInfo.mFieldId.SetValue(Test::MockAttributeId(3)); + + app::ConcreteAttributePath path; + P paths[] = { + P(0xFFFD, 0xFFF1'0003, 0xFFF1'0003), + }; + + size_t index = 0; + + for (app::PathIterator iter(&clusInfo); iter.Get(path); iter.Proceed()) + { + ChipLogDetail(AppServer, "Visited Attribute: 0x%04" PRIX16 " / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, + ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); + // NL_TEST_ASSERT(apSuite, index < ArraySize(paths) && paths[index] == path); + index++; + } + NL_TEST_ASSERT(apSuite, index == ArraySize(paths)); +} + +void TestWildcardCluster(nlTestSuite * apSuite, void * apContext) +{ + app::ClusterInfo clusInfo; + clusInfo.mEndpointId.SetValue(Test::kMockEndpoint3); + clusInfo.mFieldId.SetValue(app::Clusters::Globals::Attributes::ClusterRevision::Id); + + app::ConcreteAttributePath path; + P paths[] = { + P(0xFFFC, 0xFFF1'0001, 0x0000'FFFD), + P(0xFFFC, 0xFFF1'0002, 0x0000'FFFD), + P(0xFFFC, 0xFFF1'0003, 0x0000'FFFD), + P(0xFFFC, 0xFFF1'0004, 0x0000'FFFD), + }; + + size_t index = 0; + + for (app::PathIterator iter(&clusInfo); iter.Get(path); iter.Proceed()) + { + ChipLogDetail(AppServer, "Visited Attribute: 0x%04" PRIX16 " / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, + ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); + // NL_TEST_ASSERT(apSuite, index < ArraySize(paths) && paths[index] == path); + index++; + } + NL_TEST_ASSERT(apSuite, index == ArraySize(paths)); +} + +void TestWildcardAttribute(nlTestSuite * apSuite, void * apContext) +{ + app::ClusterInfo clusInfo; + clusInfo.mEndpointId.SetValue(Test::kMockEndpoint2); + clusInfo.mClusterId.SetValue(Test::MockClusterId(3)); + + app::ConcreteAttributePath path; + P paths[] = { + P(0xFFFD, 0xFFF1'0003, 0x0000'FFFD), P(0xFFFD, 0xFFF1'0003, 0x0000'FFFC), P(0xFFFD, 0xFFF1'0003, 0xFFF1'0001), + P(0xFFFD, 0xFFF1'0003, 0xFFF1'0002), P(0xFFFD, 0xFFF1'0003, 0xFFF1'0003), + }; + + size_t index = 0; + + for (app::PathIterator iter(&clusInfo); iter.Get(path); iter.Proceed()) + { + ChipLogDetail(AppServer, "Visited Attribute: 0x%04" PRIX16 " / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, + ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); + // NL_TEST_ASSERT(apSuite, index < ArraySize(paths) && paths[index] == path); + index++; + } + NL_TEST_ASSERT(apSuite, index == ArraySize(paths)); +} + +void TestNoWildcard(nlTestSuite * apSuite, void * apContext) +{ + app::ClusterInfo clusInfo; + clusInfo.mEndpointId.SetValue(Test::kMockEndpoint2); + clusInfo.mClusterId.SetValue(Test::MockClusterId(3)); + clusInfo.mFieldId.SetValue(Test::MockAttributeId(3)); + + app::ConcreteAttributePath path; + P paths[] = { + P(0xFFFD, 0xFFF1'0003, 0xFFF1'0003), + }; + + size_t index = 0; + + for (app::PathIterator iter(&clusInfo); iter.Get(path); iter.Proceed()) + { + ChipLogDetail(AppServer, "Visited Attribute: 0x%04" PRIX16 " / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, + ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); + // NL_TEST_ASSERT(apSuite, index < ArraySize(paths) && paths[index] == path); + index++; + } + NL_TEST_ASSERT(apSuite, index == ArraySize(paths)); +} + +void TestMultipleClusInfo(nlTestSuite * apSuite, void * apContext) +{ + + app::ClusterInfo clusInfo1; + + app::ClusterInfo clusInfo2; + clusInfo2.mClusterId.SetValue(Test::MockClusterId(3)); + clusInfo2.mFieldId.SetValue(Test::MockAttributeId(3)); + + app::ClusterInfo clusInfo3; + clusInfo3.mEndpointId.SetValue(Test::kMockEndpoint3); + clusInfo3.mFieldId.SetValue(app::Clusters::Globals::Attributes::ClusterRevision::Id); + + app::ClusterInfo clusInfo4; + clusInfo4.mEndpointId.SetValue(Test::kMockEndpoint2); + clusInfo4.mClusterId.SetValue(Test::MockClusterId(3)); + + app::ClusterInfo clusInfo5; + clusInfo5.mEndpointId.SetValue(Test::kMockEndpoint2); + clusInfo5.mClusterId.SetValue(Test::MockClusterId(3)); + clusInfo5.mFieldId.SetValue(Test::MockAttributeId(3)); + + clusInfo1.mpNext = &clusInfo2; + clusInfo2.mpNext = &clusInfo3; + clusInfo3.mpNext = &clusInfo4; + clusInfo4.mpNext = &clusInfo5; + + app::ConcreteAttributePath path; + P paths[] = { + P(0xFFFE, 0xFFF1'0001, 0x0000'FFFD), P(0xFFFE, 0xFFF1'0001, 0x0000'FFFC), P(0xFFFE, 0xFFF1'0002, 0x0000'FFFD), + P(0xFFFE, 0xFFF1'0002, 0x0000'FFFC), P(0xFFFE, 0xFFF1'0002, 0xFFF1'0001), P(0xFFFD, 0xFFF1'0001, 0x0000'FFFD), + P(0xFFFD, 0xFFF1'0001, 0x0000'FFFC), P(0xFFFD, 0xFFF1'0002, 0x0000'FFFD), P(0xFFFD, 0xFFF1'0002, 0x0000'FFFC), + P(0xFFFD, 0xFFF1'0002, 0xFFF1'0001), P(0xFFFD, 0xFFF1'0002, 0xFFF1'0002), P(0xFFFD, 0xFFF1'0003, 0x0000'FFFD), + P(0xFFFD, 0xFFF1'0003, 0x0000'FFFC), P(0xFFFD, 0xFFF1'0003, 0xFFF1'0001), P(0xFFFD, 0xFFF1'0003, 0xFFF1'0002), + P(0xFFFD, 0xFFF1'0003, 0xFFF1'0003), P(0xFFFC, 0xFFF1'0001, 0x0000'FFFD), P(0xFFFC, 0xFFF1'0001, 0x0000'FFFC), + P(0xFFFC, 0xFFF1'0001, 0xFFF1'0001), P(0xFFFC, 0xFFF1'0002, 0x0000'FFFD), P(0xFFFC, 0xFFF1'0002, 0x0000'FFFC), + P(0xFFFC, 0xFFF1'0002, 0xFFF1'0001), P(0xFFFC, 0xFFF1'0002, 0xFFF1'0002), P(0xFFFC, 0xFFF1'0002, 0xFFF1'0003), + P(0xFFFC, 0xFFF1'0002, 0xFFF1'0004), P(0xFFFC, 0xFFF1'0003, 0x0000'FFFD), P(0xFFFC, 0xFFF1'0003, 0x0000'FFFC), + P(0xFFFC, 0xFFF1'0004, 0x0000'FFFD), P(0xFFFC, 0xFFF1'0004, 0x0000'FFFC), P(0xFFFD, 0xFFF1'0003, 0xFFF1'0003), + P(0xFFFC, 0xFFF1'0001, 0x0000'FFFD), P(0xFFFC, 0xFFF1'0002, 0x0000'FFFD), P(0xFFFC, 0xFFF1'0003, 0x0000'FFFD), + P(0xFFFC, 0xFFF1'0004, 0x0000'FFFD), P(0xFFFD, 0xFFF1'0003, 0x0000'FFFD), P(0xFFFD, 0xFFF1'0003, 0x0000'FFFC), + P(0xFFFD, 0xFFF1'0003, 0xFFF1'0001), P(0xFFFD, 0xFFF1'0003, 0xFFF1'0002), P(0xFFFD, 0xFFF1'0003, 0xFFF1'0003), + P(0xFFFD, 0xFFF1'0003, 0xFFF1'0003), + }; + + size_t index = 0; + + for (app::PathIterator iter(&clusInfo1); iter.Get(path); iter.Proceed()) + { + ChipLogDetail(AppServer, "Visited Attribute: 0x%04" PRIX16 " / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, + ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); + // NL_TEST_ASSERT(apSuite, index < ArraySize(paths) && paths[index] == path); + index++; + } + NL_TEST_ASSERT(apSuite, index == ArraySize(paths)); +} + +static int TestSetup(void * inContext) +{ + return SUCCESS; +} + +/** + * Tear down the test suite. + */ +static int TestTeardown(void * inContext) +{ + return SUCCESS; +} + +/** + * Test Suite. It lists all the test functions. + */ + +// clang-format off +const nlTest sTests[] = +{ + NL_TEST_DEF("TestAllWildcard", TestAllWildcard), + NL_TEST_DEF("TestWildcardEndpoint", TestWildcardEndpoint), + NL_TEST_DEF("TestWildcardCluster", TestWildcardCluster), + NL_TEST_DEF("TestWildcardAttribute", TestWildcardAttribute), + NL_TEST_DEF("TestNoWildcard", TestNoWildcard), + NL_TEST_DEF("TestMultipleClusInfo", TestMultipleClusInfo), + NL_TEST_SENTINEL() +}; +// clang-format on + +// clang-format off +nlTestSuite sSuite = +{ + "TestPathIterator", + &sTests[0], + TestSetup, + TestTeardown, +}; +// clang-format on + +} // namespace + +int TestPathIterator() +{ + nlTestRunner(&sSuite, nullptr); + return (nlTestRunnerStats(&sSuite)); +} + +CHIP_REGISTER_TEST_SUITE(TestPathIterator) diff --git a/src/app/util/attribute-storage.cpp b/src/app/util/attribute-storage.cpp index d600c92748d997..9cf1e1b1717b05 100644 --- a/src/app/util/attribute-storage.cpp +++ b/src/app/util/attribute-storage.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -1143,6 +1144,19 @@ EmberAfCluster * emberAfGetNthCluster(EndpointId endpoint, uint8_t n, bool serve return NULL; } +// Returns the cluster id of Nth server or client cluster, +// depending on server toggle. +// Returns MEI::kWildcard if cluster does not exists. +ClusterId emberAfGetNthClusterId(EndpointId endpoint, uint8_t n, bool server) +{ + EmberAfCluster * cluster = emberAfGetNthCluster(endpoint, n, server); + if (cluster == nullptr) + { + return app::ConcreteAttributePath::kInvalidClusterId; + } + return cluster->clusterId; +} + // Returns number of clusters put into the passed cluster list // for the given endpoint and client/server polarity uint8_t emberAfGetClustersFromEndpoint(EndpointId endpoint, ClusterId * clusterList, uint8_t listLen, bool server) @@ -1357,3 +1371,37 @@ app::AttributeAccessInterface * findAttributeAccessOverride(EndpointId endpointI return nullptr; } + +uint16_t emberAfGetServerAttributeCount(chip::EndpointId endpoint, chip::ClusterId cluster) +{ + EmberAfCluster * clusterObj = emberAfFindCluster(endpoint, cluster, CLUSTER_MASK_SERVER); + VerifyOrReturnError(clusterObj != nullptr, 0); + return clusterObj->attributeCount; +} + +uint16_t emberAfGetServerAttributeIndexByAttributeId(chip::EndpointId endpoint, chip::ClusterId cluster, + chip::AttributeId attributeId) +{ + EmberAfCluster * clusterObj = emberAfFindCluster(endpoint, cluster, CLUSTER_MASK_SERVER); + VerifyOrReturnError(clusterObj != nullptr, UINT16_MAX); + + for (uint16_t i = 0; i < clusterObj->attributeCount; i++) + { + if (clusterObj->attributes[i].attributeId == attributeId) + { + return i; + } + } + return UINT16_MAX; +} + +chip::AttributeId emberAfGetServerAttributeIdByIndex(chip::EndpointId endpoint, chip::ClusterId cluster, uint16_t attributeIndex) +{ + EmberAfCluster * clusterObj = emberAfFindCluster(endpoint, cluster, CLUSTER_MASK_SERVER); + VerifyOrReturnError(clusterObj != nullptr, 0); + if (clusterObj->attributeCount <= attributeIndex) + { + return app::ConcreteAttributePath::kInvalidAttributeId; + } + return clusterObj->attributes[attributeIndex].attributeId; +} diff --git a/src/app/util/attribute-storage.h b/src/app/util/attribute-storage.h index 8a4b9075cf43d0..3486e71899e320 100644 --- a/src/app/util/attribute-storage.h +++ b/src/app/util/attribute-storage.h @@ -149,10 +149,15 @@ uint8_t emberAfClusterIndex(chip::EndpointId endpoint, chip::ClusterId clusterId // otherwise number of client clusters on this endpoint uint8_t emberAfClusterCount(chip::EndpointId endpoint, bool server); -// Returns the clusterId of Nth server or client cluster, +// Returns the cluster of Nth server or client cluster, // depending on server toggle. EmberAfCluster * emberAfGetNthCluster(chip::EndpointId endpoint, uint8_t n, bool server); +// Returns the clusterId of Nth server or client cluster, +// depending on server toggle. +// Returns app::ConcreteAttributePath::kInvalidClusterId if cluster does not exists. +chip::ClusterId emberAfGetNthClusterId(chip::EndpointId endpoint, uint8_t n, bool server); + // Returns number of clusters put into the passed cluster list // for the given endpoint and client/server polarity uint8_t emberAfGetClustersFromEndpoint(chip::EndpointId endpoint, chip::ClusterId * clusterList, uint8_t listLen, bool server); @@ -240,7 +245,7 @@ bool emberAfEndpointIsEnabled(chip::EndpointId endpoint); // and those indexes may be DIFFERENT than the indexes returned from // emberAfGetNthCluster(). In other words: // -// - Use emberAfGetClustersFromEndpoint() with emberAfGetNthCluster() +// - Use emberAfGetClustersFromEndpoint() with emberAfGetNthCluster() amberAfGetNthClusterId() // - Use emberAfGetClusterCountForEndpoint() with emberAfGetClusterByIndex() // // Don't mix them. @@ -253,6 +258,22 @@ EmberAfStatus emberAfSetDynamicEndpoint(uint16_t index, chip::EndpointId id, Emb chip::EndpointId emberAfClearDynamicEndpoint(uint16_t index); uint16_t emberAfGetDynamicIndexFromEndpoint(chip::EndpointId id); +// Get the number of attributs of the specific cluster under the endpoint. Is useful for the application to get the basic infomation +// of or iterate over the attributes. +// Returns 0 if the cluster or endpoint do not exists. +uint16_t emberAfGetServerAttributeCount(chip::EndpointId endpoint, chip::ClusterId cluster); + +// Get the index of attribut of the specific cluster under the endpoint. Is useful for the application to get the basic infomation +// of or iterate over the attributes. +// Returns UINT16_MAX if the attribute, cluster or endpoint do not exists. +uint16_t emberAfGetServerAttributeIndexByAttributeId(chip::EndpointId endpoint, chip::ClusterId cluster, + chip::AttributeId attributeId); + +// Get the attribute id at the attributeIndex of the cluster under the endpoint. This function is useful for iterating over the +// attributes. +// Returns chip::ConcreteAttributePath::kInvalidAttributeId if the attribute does not exists. +chip::AttributeId emberAfGetServerAttributeIdByIndex(chip::EndpointId endpoint, chip::ClusterId cluster, uint16_t attributeIndex); + /** * Register an attribute access override. It will remain registered until * the endpoint it's registered for is disabled (or until shutdown if it's diff --git a/src/app/util/mock/BUILD.gn b/src/app/util/mock/BUILD.gn new file mode 100644 index 00000000000000..4ffcbf1c85034d --- /dev/null +++ b/src/app/util/mock/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Project CHIP Authors +# +# 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. + +import("//build_overrides/chip.gni") + +source_set("mock_ember") { + sources = [ "attribute-storage.cpp" ] + + public_deps = [ + "${chip_root}/src/app", + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/support", + ] + + cflags = [ "-Wconversion" ] +} diff --git a/src/app/util/mock/Constants.h b/src/app/util/mock/Constants.h new file mode 100644 index 00000000000000..2c515c8ebf1e95 --- /dev/null +++ b/src/app/util/mock/Constants.h @@ -0,0 +1,45 @@ +/* + * + * 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 contains the constants for the mocked attribute-storage.cpp + */ + +#pragma once + +#include + +namespace chip { +namespace Test { +constexpr EndpointId kMockEndpoint1 = 0xFFFE; +constexpr EndpointId kMockEndpoint2 = 0xFFFD; +constexpr EndpointId kMockEndpoint3 = 0xFFFC; + +constexpr AttributeId MockAttributeId(const uint16_t & id) +{ + return (0xFFF1'0000 | id); +} + +constexpr AttributeId MockClusterId(const uint16_t & id) +{ + return (0xFFF1'0000 | id); +} + +} // namespace Test +} // namespace chip diff --git a/src/app/util/mock/attribute-storage.cpp b/src/app/util/mock/attribute-storage.cpp new file mode 100644 index 00000000000000..2312cfcf1441de --- /dev/null +++ b/src/app/util/mock/attribute-storage.cpp @@ -0,0 +1,186 @@ +/* + * + * 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 contains the mock implementation for the generated attribute-storage.cpp + * - It contains three endpoints, 0xFFFE, 0xFFFD, 0xFFFC + * - It contains four clusters: 0xFFF1'0001 to 0xFFF1'0004 + * - All cluster has two global attribute (0x0000'FFFC, 0x0000'FFFD) + * - Some clusters has some cluster-specific attributes, with 0xFFF1 prefix. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Mock Functions +typedef uint8_t EmberAfClusterMask; +#define CLUSTER_MASK_SERVER (0x40) + +using namespace chip; +using namespace chip::Test; +using namespace chip::app; + +namespace { + +EndpointId endpoints[] = { kMockEndpoint1, kMockEndpoint2, kMockEndpoint3 }; +uint16_t clusterIndex[] = { 0, 2, 5 }; +uint8_t clusterCount[] = { 2, 3, 4 }; +ClusterId clusters[] = { MockClusterId(1), MockClusterId(2), MockClusterId(1), MockClusterId(2), MockClusterId(3), + MockClusterId(1), MockClusterId(2), MockClusterId(3), MockClusterId(4) }; +uint16_t attributeIndex[] = { 0, 2, 5, 7, 11, 16, 19, 25, 27 }; +uint16_t attributeCount[] = { 2, 3, 2, 4, 5, 3, 6, 2, 2 }; +AttributeId attributes[] = { + // clang-format off + Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, + Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, MockAttributeId(1), + Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, + Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, MockAttributeId(1), MockAttributeId(2), + Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, MockAttributeId(1), MockAttributeId(2), MockAttributeId(3), + Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, MockAttributeId(1), + Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, MockAttributeId(1), MockAttributeId(2), MockAttributeId(3), MockAttributeId(4), + Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, + Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id + // clang-format on +}; + +} // namespace + +uint16_t emberAfEndpointCount(void) +{ + return ArraySize(endpoints); +} + +uint16_t emberAfIndexFromEndpoint(chip::EndpointId endpoint) +{ + for (uint16_t i = 0; i < ArraySize(endpoints); i++) + { + if (endpoints[i] == endpoint) + { + return i; + } + } + return UINT16_MAX; +} + +uint8_t emberAfClusterCount(chip::EndpointId endpoint, bool server) +{ + for (uint16_t i = 0; i < ArraySize(endpoints); i++) + { + if (endpoints[i] == endpoint) + { + return clusterCount[i]; + } + } + return 0; +} + +uint16_t emberAfGetServerAttributeCount(chip::EndpointId endpoint, chip::ClusterId cluster) +{ + uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint); + uint16_t clusterCountOnEndpoint = emberAfClusterCount(endpoint, true); + for (uint16_t i = 0; i < clusterCountOnEndpoint; i++) + { + if (clusters[i + clusterIndex[endpointIndex]] == cluster) + { + return attributeCount[i + clusterIndex[endpointIndex]]; + } + } + return UINT16_MAX; +} + +uint16_t emberAfGetServerAttributeIndexByAttributeId(chip::EndpointId endpoint, chip::ClusterId cluster, + chip::AttributeId attributeId) +{ + uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint); + uint16_t clusterCountOnEndpoint = emberAfClusterCount(endpoint, true); + for (uint16_t i = 0; i < clusterCountOnEndpoint; i++) + { + if (clusters[i + clusterIndex[endpointIndex]] == cluster) + { + uint16_t clusterAttributeOffset = attributeIndex[i + clusterIndex[endpointIndex]]; + for (uint16_t j = 0; j < emberAfGetServerAttributeCount(endpoint, cluster); j++) + { + if (attributes[clusterAttributeOffset + j] == attributeId) + { + return j; + } + } + return UINT16_MAX; + } + } + return UINT16_MAX; +} + +chip::EndpointId emberAfEndpointFromIndex(uint16_t index) +{ + return index < ArraySize(endpoints) ? endpoints[index] : app::ConcreteAttributePath::kInvalidEndpointId; +} + +chip::ClusterId emberAfGetNthClusterId(chip::EndpointId endpoint, uint8_t n, bool server) +{ + if (n >= emberAfClusterCount(endpoint, server)) + { + return app::ConcreteAttributePath::kInvalidClusterId; + } + return clusters[clusterIndex[emberAfIndexFromEndpoint(endpoint)] + n]; +} + +chip::AttributeId emberAfGetServerAttributeIdByIndex(chip::EndpointId endpoint, chip::ClusterId cluster, uint16_t index) +{ + uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint); + uint16_t clusterCountOnEndpoint = emberAfClusterCount(endpoint, true); + for (uint16_t i = 0; i < clusterCountOnEndpoint; i++) + { + if (clusters[i + clusterIndex[endpointIndex]] == cluster) + { + uint16_t clusterAttributeOffset = attributeIndex[i + clusterIndex[endpointIndex]]; + if (index >= emberAfGetServerAttributeCount(endpoint, cluster)) + { + return app::ConcreteAttributePath::kInvalidAttributeId; + } + return attributes[clusterAttributeOffset + index]; + } + } + return app::ConcreteAttributePath::kInvalidAttributeId; +} + +uint8_t emberAfClusterIndex(chip::EndpointId endpoint, chip::ClusterId cluster, EmberAfClusterMask mask) +{ + uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint); + uint16_t clusterCountOnEndpoint = emberAfClusterCount(endpoint, true); + for (uint8_t i = 0; i < clusterCountOnEndpoint; i++) + { + if (clusters[i + clusterIndex[endpointIndex]] == cluster) + { + return i; + } + } + return UINT8_MAX; +}