From 2165682eaaf50270048ab3245b151b9c7f44994f Mon Sep 17 00:00:00 2001 From: yunhanw-google Date: Wed, 28 Apr 2021 05:43:51 -0700 Subject: [PATCH] Add IM/Ember Read/Writer Interface and attribute read for IM (#6214) Summary of Changes: -- Add initial IM read attribute implementation and ember read/write interface so that read client can send IM read request with attribute path, interaction model engine receive this request and ask reporting engine generate the corresponding interested attributes and send it back via read hander. -- Add unit and cirque integration test --- src/app/AttributePathParams.h | 10 +- src/app/ClusterInfo.h | 4 +- src/app/CommandHandler.cpp | 12 +- src/app/InteractionModelEngine.cpp | 71 ++++++++ src/app/InteractionModelEngine.h | 13 +- src/app/ReadClient.cpp | 140 +++++++++++++--- src/app/ReadClient.h | 7 +- src/app/ReadHandler.cpp | 54 +++++++ src/app/ReadHandler.h | 6 +- src/app/reporting/Engine.cpp | 65 ++++++++ src/app/reporting/Engine.h | 3 + src/app/tests/BUILD.gn | 1 + src/app/tests/TestClusterInfo.cpp | 2 +- src/app/tests/TestInteractionModelEngine.cpp | 151 ++++++++++++++++++ src/app/tests/TestReadInteraction.cpp | 3 +- src/app/tests/TestReportingEngine.cpp | 38 +++++ .../tests/integration/chip_im_initiator.cpp | 23 ++- .../tests/integration/chip_im_responder.cpp | 33 +++- src/app/tests/integration/common.h | 7 +- 19 files changed, 582 insertions(+), 61 deletions(-) create mode 100644 src/app/tests/TestInteractionModelEngine.cpp diff --git a/src/app/AttributePathParams.h b/src/app/AttributePathParams.h index cfde12a9899ccc..367610abc1456a 100644 --- a/src/app/AttributePathParams.h +++ b/src/app/AttributePathParams.h @@ -36,15 +36,7 @@ struct AttributePathParams mNodeId(aNodeId), mEndpointId(aEndpointId), mClusterId(aClusterId), mFieldId(aFieldId), mListIndex(aListIndex), mFlags(aFlags) {} - AttributePathParams(const AttributePathParams & aAttributePathParams) - { - mNodeId = aAttributePathParams.mNodeId; - mEndpointId = aAttributePathParams.mEndpointId; - mClusterId = aAttributePathParams.mClusterId; - mFieldId = aAttributePathParams.mFieldId; - mListIndex = aAttributePathParams.mListIndex; - mFlags = aAttributePathParams.mFlags; - } + AttributePathParams() {} bool IsSamePath(const AttributePathParams & other) const { if (other.mNodeId != mNodeId || other.mEndpointId != mEndpointId || other.mClusterId != mClusterId) diff --git a/src/app/ClusterInfo.h b/src/app/ClusterInfo.h index 85cf5496d32b26..6f65438969470b 100644 --- a/src/app/ClusterInfo.h +++ b/src/app/ClusterInfo.h @@ -28,12 +28,14 @@ struct ClusterInfo ClusterInfo(const AttributePathParams & aAttributePathParams, bool aDirty) : mAttributePathParams(aAttributePathParams), mDirty(aDirty) {} + ClusterInfo() {} bool IsDirty() { return mDirty; } void SetDirty() { mDirty = true; } void ClearDirty() { mDirty = false; } bool IsSamePath(const ClusterInfo & other) const { return other.mAttributePathParams.IsSamePath(mAttributePathParams); } AttributePathParams mAttributePathParams; - bool mDirty = false; + bool mDirty = false; + ClusterInfo * mpNext = nullptr; }; } // namespace app } // namespace chip diff --git a/src/app/CommandHandler.cpp b/src/app/CommandHandler.cpp index dd48804073ad3b..480efaa72039d8 100644 --- a/src/app/CommandHandler.cpp +++ b/src/app/CommandHandler.cpp @@ -82,10 +82,14 @@ CHIP_ERROR CommandHandler::ProcessCommandDataElement(CommandDataElement::Parser chip::CommandId commandId; chip::EndpointId endpointId; - SuccessOrExit(aCommandElement.GetCommandPath(&commandPath)); - SuccessOrExit(commandPath.GetClusterId(&clusterId)); - SuccessOrExit(commandPath.GetCommandId(&commandId)); - SuccessOrExit(commandPath.GetEndpointId(&endpointId)); + err = aCommandElement.GetCommandPath(&commandPath); + SuccessOrExit(err); + err = commandPath.GetClusterId(&clusterId); + SuccessOrExit(err); + err = commandPath.GetCommandId(&commandId); + SuccessOrExit(err); + err = commandPath.GetEndpointId(&endpointId); + SuccessOrExit(err); err = aCommandElement.GetData(&commandDataReader); if (CHIP_END_OF_TLV == err) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index e243b7a621e2b4..496c788b441977 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -53,6 +53,13 @@ CHIP_ERROR InteractionModelEngine::Init(Messaging::ExchangeManager * apExchangeM mReportingEngine.Init(); SuccessOrExit(err); + for (uint32_t index = 0; index < IM_SERVER_MAX_NUM_PATH_GROUPS - 1; index++) + { + mClusterInfoPool[index].mpNext = &mClusterInfoPool[index + 1]; + } + mClusterInfoPool[IM_SERVER_MAX_NUM_PATH_GROUPS - 1].mpNext = nullptr; + mpNextAvailableClusterInfo = mClusterInfoPool; + exit: return err; } @@ -78,6 +85,13 @@ void InteractionModelEngine::Shutdown() { readHandler.Shutdown(); } + + for (uint32_t index = 0; index < IM_SERVER_MAX_NUM_PATH_GROUPS; index++) + { + mClusterInfoPool[index].mpNext = nullptr; + mClusterInfoPool[index].ClearDirty(); + } + mpNextAvailableClusterInfo = nullptr; } CHIP_ERROR InteractionModelEngine::NewCommandSender(CommandSender ** const apCommandSender) @@ -240,9 +254,66 @@ DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aComman "Default DispatchSingleClusterCommand is called, this should be replaced by actual dispatched for cluster commands"); } +CHIP_ERROR __attribute__((weak)) ReadSingleClusterData(AttributePathParams & aAttributePathParams, TLV::TLVWriter & aWriter) +{ + ChipLogDetail(DataManagement, + "Received Cluster Command: Cluster=%" PRIx16 " NodeId=%" PRIx64 " Endpoint=%" PRIx8 " FieldId=%" PRIx8 + " ListIndex=%" PRIx8, + aAttributePathParams.mClusterId, aAttributePathParams.mNodeId, aAttributePathParams.mEndpointId, + aAttributePathParams.mFieldId, aAttributePathParams.mListIndex); + ChipLogError(DataManagement, + "Default ReadSingleClusterData is called, this should be replaced by actual dispatched for cluster"); + return CHIP_NO_ERROR; +} + +CHIP_ERROR __attribute__((weak)) WriteSingleClusterData(AttributePathParams & aAttributePathParams, TLV::TLVReader & aReader) +{ + ChipLogDetail(DataManagement, + "Received Cluster Attribute: Cluster=%" PRIx16 " NodeId=%" PRIx64 " Endpoint=%" PRIx8 " FieldId=%" PRIx8, + " ListIndex=%" PRIx8, aAttributePathParams.mClusterId, aAttributePathParams.mNodeId, + aAttributePathParams.mEndpointId, aAttributePathParams.mFieldId, aAttributePathParams.mListIndex); + ChipLogError(DataManagement, + "Default WriteSingleClusterData is called, this should be replaced by actual dispatched for cluster"); + return CHIP_NO_ERROR; +} + uint16_t InteractionModelEngine::GetReadClientArrayIndex(const ReadClient * const apReadClient) const { return static_cast(apReadClient - mReadClients); } + +void InteractionModelEngine::ReleaseClusterInfoList(ClusterInfo *& aClusterInfo) +{ + ClusterInfo * lastClusterInfo = aClusterInfo; + if (lastClusterInfo == nullptr) + { + return; + } + + while (lastClusterInfo != nullptr && lastClusterInfo->mpNext != nullptr) + { + lastClusterInfo->ClearDirty(); + lastClusterInfo = lastClusterInfo->mpNext; + } + lastClusterInfo->ClearDirty(); + lastClusterInfo->mpNext = mpNextAvailableClusterInfo; + mpNextAvailableClusterInfo = aClusterInfo; + aClusterInfo = nullptr; +} + +CHIP_ERROR InteractionModelEngine::PushFront(ClusterInfo *& aClusterInfo, AttributePathParams & aAttributePathParams) +{ + ClusterInfo * last = aClusterInfo; + if (mpNextAvailableClusterInfo == nullptr) + { + return CHIP_ERROR_NO_MEMORY; + } + aClusterInfo = mpNextAvailableClusterInfo; + mpNextAvailableClusterInfo = mpNextAvailableClusterInfo->mpNext; + aClusterInfo->mpNext = last; + aClusterInfo->mAttributePathParams = aAttributePathParams; + return CHIP_NO_ERROR; +} + } // namespace app } // namespace chip diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index 820789e3a669b4..df5b0511c16775 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -44,19 +45,21 @@ #include #include #include +#include #define CHIP_MAX_NUM_COMMAND_HANDLER 1 #define CHIP_MAX_NUM_COMMAND_SENDER 1 #define CHIP_MAX_NUM_READ_CLIENT 1 #define CHIP_MAX_NUM_READ_HANDLER 1 #define CHIP_MAX_REPORTS_IN_FLIGHT 1 +#define IM_SERVER_MAX_NUM_PATH_GROUPS 256 namespace chip { namespace app { constexpr size_t kMaxSecureSduLengthBytes = 1024; constexpr uint32_t kImMessageTimeoutMsec = 3000; - +constexpr FieldId kRootFieldId = 0; /** * @class InteractionModelEngine * @@ -127,6 +130,9 @@ class InteractionModelEngine : public Messaging::ExchangeDelegate reporting::Engine & GetReportingEngine() { return mReportingEngine; } + void ReleaseClusterInfoList(ClusterInfo *& aClusterInfo); + CHIP_ERROR PushFront(ClusterInfo *& aClusterInfo, AttributePathParams & aAttributePathParams); + private: friend class reporting::Engine; void OnUnknownMsgType(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, @@ -151,10 +157,13 @@ class InteractionModelEngine : public Messaging::ExchangeDelegate ReadClient mReadClients[CHIP_MAX_NUM_READ_CLIENT]; ReadHandler mReadHandlers[CHIP_MAX_NUM_READ_HANDLER]; reporting::Engine mReportingEngine; + ClusterInfo mClusterInfoPool[IM_SERVER_MAX_NUM_PATH_GROUPS]; + ClusterInfo * mpNextAvailableClusterInfo = nullptr; }; void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aCommandId, chip::EndpointId aEndPointId, chip::TLV::TLVReader & aReader, Command * apCommandObj); - +CHIP_ERROR ReadSingleClusterData(AttributePathParams & aAttributePathParams, TLV::TLVWriter & aWriter); +CHIP_ERROR WriteSingleClusterData(AttributePathParams & aAttributePathParams, TLV::TLVReader & aReader); } // namespace app } // namespace chip diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp index c4fd9583a772ed..9e385eed3f0fbb 100644 --- a/src/app/ReadClient.cpp +++ b/src/app/ReadClient.cpp @@ -78,7 +78,8 @@ void ReadClient::MoveToState(const ClientState aTargetState) } CHIP_ERROR ReadClient::SendReadRequest(NodeId aNodeId, Transport::AdminId aAdminId, EventPathParams * apEventPathParamsList, - size_t aEventPathParamsListSize) + size_t aEventPathParamsListSize, AttributePathParams * apAttributePathParamsList, + size_t aAttributePathParamsListSize) { CHIP_ERROR err = CHIP_NO_ERROR; System::PacketBufferHandle msgBuf; @@ -104,6 +105,33 @@ CHIP_ERROR ReadClient::SendReadRequest(NodeId aNodeId, Transport::AdminId aAdmin { // TODO: fill to construct event paths } + + if (aAttributePathParamsListSize != 0 && apAttributePathParamsList != nullptr) + { + AttributePathList::Builder attributePathListBuilder = request.CreateAttributePathListBuilder(); + SuccessOrExit(attributePathListBuilder.GetError()); + for (size_t index = 0; index < aAttributePathParamsListSize; index++) + { + AttributePath::Builder attributePathBuilder = attributePathListBuilder.CreateAttributePathBuilder(); + attributePathBuilder.NodeId(apAttributePathParamsList[index].mNodeId) + .EndpointId(apAttributePathParamsList[index].mEndpointId) + .ClusterId(apAttributePathParamsList[index].mClusterId); + if (apAttributePathParamsList[index].mFlags == AttributePathFlags::kFieldIdValid) + { + attributePathBuilder.FieldId(apAttributePathParamsList[index].mFieldId); + } + else if (apAttributePathParamsList[index].mFlags == AttributePathFlags::kListIndexValid) + { + attributePathBuilder.ListIndex(apAttributePathParamsList[index].mListIndex); + } + else + { + err = CHIP_ERROR_INVALID_ARGUMENT; + ExitNow(); + } + SuccessOrExit(attributePathBuilder.GetError()); + } + } request.EndOfReadRequest(); SuccessOrExit(request.GetError()); @@ -139,7 +167,6 @@ void ReadClient::OnMessageReceived(Messaging::ExchangeContext * apExchangeContex ClearExistingExchangeContext(); MoveToState(ClientState::Initialized); - if (mpDelegate != nullptr) { if (err != CHIP_NO_ERROR) @@ -171,10 +198,12 @@ CHIP_ERROR ReadClient::ProcessReportData(System::PacketBufferHandle aPayload) CHIP_ERROR err = CHIP_NO_ERROR; ReportData::Parser report; - bool isEventListPresent = false; - bool suppressResponse = false; - bool moreChunkedMessages = false; - + bool isEventListPresent = false; + bool isAttributeDataListPresent = false; + bool suppressResponse = false; + bool moreChunkedMessages = false; + EventList::Parser eventList; + AttributeDataList::Parser attributeDataList; System::PacketBufferTLVReader reader; reader.Init(std::move(aPayload)); @@ -202,30 +231,35 @@ CHIP_ERROR ReadClient::ProcessReportData(System::PacketBufferHandle aPayload) } SuccessOrExit(err); + err = report.GetEventDataList(&eventList); + isEventListPresent = (err == CHIP_NO_ERROR); + if (err == CHIP_END_OF_TLV) { - EventList::Parser eventList; + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); - err = report.GetEventDataList(&eventList); - if (CHIP_NO_ERROR == err) - { - isEventListPresent = true; - } - else if (CHIP_END_OF_TLV == err) - { - isEventListPresent = false; - err = CHIP_NO_ERROR; - } + if (isEventListPresent && nullptr != mpDelegate) + { + chip::TLV::TLVReader eventListReader; + eventList.GetReader(&eventListReader); + err = mpDelegate->EventStreamReceived(mpExchangeCtx, &eventListReader); SuccessOrExit(err); + } - VerifyOrExit(moreChunkedMessages == false, err = CHIP_ERROR_MESSAGE_INCOMPLETE); - - if (isEventListPresent && nullptr != mpDelegate) - { - chip::TLV::TLVReader eventListReader; - eventList.GetReader(&eventListReader); - err = mpDelegate->EventStreamReceived(mpExchangeCtx, &eventListReader); - SuccessOrExit(err); - } + err = report.GetAttributeDataList(&attributeDataList); + isAttributeDataListPresent = (err == CHIP_NO_ERROR); + if (err == CHIP_END_OF_TLV) + { + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); + if (isAttributeDataListPresent && nullptr != mpDelegate && !moreChunkedMessages) + { + chip::TLV::TLVReader attributeDataListReader; + attributeDataList.GetReader(&attributeDataListReader); + err = ProcessAttributeDataList(attributeDataListReader); + SuccessOrExit(err); } if (!suppressResponse) @@ -250,5 +284,59 @@ void ReadClient::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContex mpDelegate->ReportError(this, CHIP_ERROR_TIMEOUT); } } + +CHIP_ERROR ReadClient::ProcessAttributeDataList(TLV::TLVReader & aAttributeDataListReader) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + while (CHIP_NO_ERROR == (err = aAttributeDataListReader.Next())) + { + chip::TLV::TLVReader dataReader; + AttributeDataElement::Parser element; + AttributePath::Parser attributePathParser; + AttributePathParams attributePathParams; + TLV::TLVReader reader = aAttributeDataListReader; + err = element.Init(reader); + SuccessOrExit(err); + + err = element.GetAttributePath(&attributePathParser); + SuccessOrExit(err); + + err = attributePathParser.GetNodeId(&(attributePathParams.mNodeId)); + SuccessOrExit(err); + + err = attributePathParser.GetEndpointId(&(attributePathParams.mEndpointId)); + SuccessOrExit(err); + + err = attributePathParser.GetClusterId(&(attributePathParams.mClusterId)); + SuccessOrExit(err); + + err = attributePathParser.GetFieldId(&(attributePathParams.mFieldId)); + if (CHIP_NO_ERROR == err) + { + attributePathParams.mFlags = AttributePathFlags::kFieldIdValid; + } + else if (CHIP_END_OF_TLV == err) + { + err = attributePathParser.GetListIndex(&(attributePathParams.mListIndex)); + SuccessOrExit(err); + attributePathParams.mFlags = AttributePathFlags::kListIndexValid; + } + SuccessOrExit(err); + + err = element.GetData(&dataReader); + SuccessOrExit(err); + err = WriteSingleClusterData(attributePathParams, dataReader); + SuccessOrExit(err); + } + + if (CHIP_END_OF_TLV == err) + { + err = CHIP_NO_ERROR; + } + +exit: + ChipLogFunctError(err); + return err; +} }; // namespace app }; // namespace chip diff --git a/src/app/ReadClient.h b/src/app/ReadClient.h index a4c7a9f7b24665..2f7189910fab3f 100644 --- a/src/app/ReadClient.h +++ b/src/app/ReadClient.h @@ -70,11 +70,14 @@ class ReadClient : public Messaging::ExchangeDelegate * @param[in] aAdminId Admin ID * @param[in] apEventPathParamsList a list of event paths the read client is interested in * @param[in] aEventPathParamsListSize Number of event paths in apEventPathParamsList + * @param[in] apAttributePathParamsList a list of attribute paths the read client is interested in + * @param[in] aAttributePathParamsListSize Number of attribute paths in apAttributePathParamsList * @retval #others fail to send read request * @retval #CHIP_NO_ERROR On success. */ CHIP_ERROR SendReadRequest(NodeId aNodeId, Transport::AdminId aAdminId, EventPathParams * apEventPathParamsList, - size_t aEventPathParamsListSize); + size_t aEventPathParamsListSize, AttributePathParams * apAttributePathParamsList, + size_t aAttributePathParamsListSize); private: friend class TestReadInteraction; @@ -114,6 +117,8 @@ class ReadClient : public Messaging::ExchangeDelegate */ bool IsFree() const { return mState == ClientState::Uninitialized; }; + CHIP_ERROR ProcessAttributeDataList(TLV::TLVReader & aAttributeDataListReader); + void MoveToState(const ClientState aTargetState); CHIP_ERROR ProcessReportData(System::PacketBufferHandle aPayload); CHIP_ERROR ClearExistingExchangeContext(); diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 81aa1be4339ddc..9d8441559b58d2 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -39,6 +39,7 @@ CHIP_ERROR ReadHandler::Init(InteractionModelDelegate * apDelegate) mpDelegate = apDelegate; mSuppressResponse = true; mGetToAllEvents = true; + mpClusterInfoList = nullptr; MoveToState(HandlerState::Initialized); exit: @@ -48,6 +49,7 @@ CHIP_ERROR ReadHandler::Init(InteractionModelDelegate * apDelegate) void ReadHandler::Shutdown() { + InteractionModelEngine::GetInstance()->ReleaseClusterInfoList(mpClusterInfoList); ClearExistingExchangeContext(); MoveToState(HandlerState::Uninitialized); mpDelegate = nullptr; @@ -92,6 +94,8 @@ CHIP_ERROR ReadHandler::SendReportData(System::PacketBufferHandle aPayload) err = mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::ReportData, std::move(aPayload), Messaging::SendFlags(Messaging::SendMessageFlags::kNone)); exit: + ChipLogFunctError(err); + Shutdown(); return err; } @@ -102,6 +106,7 @@ CHIP_ERROR ReadHandler::ProcessReadRequest(System::PacketBufferHandle aPayload) ReadRequest::Parser readRequestParser; EventPathList::Parser eventPathListParser; + AttributePathList::Parser attributePathListParser; TLV::TLVReader eventPathListReader; reader.Init(std::move(aPayload)); @@ -116,6 +121,18 @@ CHIP_ERROR ReadHandler::ProcessReadRequest(System::PacketBufferHandle aPayload) err = readRequestParser.CheckSchemaValidity(); SuccessOrExit(err); #endif + + err = readRequestParser.GetAttributePathList(&attributePathListParser); + if (err == CHIP_END_OF_TLV) + { + err = CHIP_NO_ERROR; + } + else + { + SuccessOrExit(err); + ProcessAttributePathList(attributePathListParser); + } + err = readRequestParser.GetEventPathList(&eventPathListParser); if (err == CHIP_END_OF_TLV) { @@ -153,6 +170,43 @@ CHIP_ERROR ReadHandler::ProcessReadRequest(System::PacketBufferHandle aPayload) return err; } +CHIP_ERROR ReadHandler::ProcessAttributePathList(AttributePathList::Parser & aAttributePathListParser) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TLV::TLVReader reader; + aAttributePathListParser.GetReader(&reader); + + while (CHIP_NO_ERROR == (err = reader.Next())) + { + VerifyOrExit(TLV::AnonymousTag == reader.GetTag(), err = CHIP_ERROR_INVALID_TLV_TAG); + VerifyOrExit(TLV::kTLVType_List == reader.GetType(), err = CHIP_ERROR_WRONG_TLV_TYPE); + AttributePathParams attributePathParams; + AttributePath::Parser path; + err = path.Init(reader); + SuccessOrExit(err); + err = path.GetNodeId(&(attributePathParams.mNodeId)); + SuccessOrExit(err); + err = path.GetEndpointId(&(attributePathParams.mEndpointId)); + SuccessOrExit(err); + err = path.GetClusterId(&(attributePathParams.mClusterId)); + SuccessOrExit(err); + err = path.GetFieldId(&(attributePathParams.mFieldId)); + SuccessOrExit(err); + err = InteractionModelEngine::GetInstance()->PushFront(mpClusterInfoList, attributePathParams); + SuccessOrExit(err); + mpClusterInfoList->SetDirty(); + } + // if we have exhausted this container + if (CHIP_END_OF_TLV == err) + { + err = CHIP_NO_ERROR; + } + +exit: + ChipLogFunctError(err); + return err; +} + const char * ReadHandler::GetStateStr() const { #if CHIP_DETAIL_LOGGING diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 9c7a5e60286a1c..2c8a3057d2d1ce 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -24,6 +24,7 @@ #pragma once +#include #include #include #include @@ -99,6 +100,8 @@ class ReadHandler virtual ~ReadHandler() = default; + ClusterInfo * GetCluterInfolist() { return mpClusterInfoList; }; + private: enum class HandlerState { @@ -108,7 +111,7 @@ class ReadHandler }; CHIP_ERROR ProcessReadRequest(System::PacketBufferHandle aPayload); - + CHIP_ERROR ProcessAttributePathList(AttributePathList::Parser & aAttributePathListParser); void MoveToState(const HandlerState aTargetState); const char * GetStateStr() const; @@ -125,6 +128,7 @@ class ReadHandler // Current Handler state HandlerState mState; + ClusterInfo * mpClusterInfoList = nullptr; }; } // namespace app } // namespace chip diff --git a/src/app/reporting/Engine.cpp b/src/app/reporting/Engine.cpp index e63530628a932c..332231ede3a8c2 100644 --- a/src/app/reporting/Engine.cpp +++ b/src/app/reporting/Engine.cpp @@ -37,6 +37,69 @@ CHIP_ERROR Engine::Init() return CHIP_NO_ERROR; } +CHIP_ERROR +Engine::RetrieveClusterData(AttributeDataElement::Builder & aAttributeDataElementBuilder, ClusterInfo & aClusterInfo) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TLV::TLVType type = TLV::kTLVType_NotSpecified; + AttributePath::Builder attributePathBuilder = aAttributeDataElementBuilder.CreateAttributePathBuilder(); + attributePathBuilder.NodeId(aClusterInfo.mAttributePathParams.mNodeId) + .EndpointId(aClusterInfo.mAttributePathParams.mEndpointId) + .ClusterId(aClusterInfo.mAttributePathParams.mClusterId) + .FieldId(aClusterInfo.mAttributePathParams.mFieldId) + .EndOfAttributePath(); + err = attributePathBuilder.GetError(); + SuccessOrExit(err); + + aAttributeDataElementBuilder.GetWriter()->StartContainer(TLV::ContextTag(AttributeDataElement::kCsTag_Data), + TLV::kTLVType_Structure, type); + err = ReadSingleClusterData(aClusterInfo.mAttributePathParams, *(aAttributeDataElementBuilder.GetWriter())); + SuccessOrExit(err); + aAttributeDataElementBuilder.GetWriter()->EndContainer(type); + aAttributeDataElementBuilder.DataVersion(0).MoreClusterData(false).EndOfAttributeDataElement(); + err = aAttributeDataElementBuilder.GetError(); + // TODO: Add DataVersion support + +exit: + aClusterInfo.ClearDirty(); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(DataManagement, "Error retrieving data from clusterId: %08x, err = %d", + aClusterInfo.mAttributePathParams.mClusterId, err); + } + + return err; +} + +CHIP_ERROR Engine::BuildSingleReportDataAttributeDataList(ReportData::Builder & reportDataBuilder, ReadHandler * apReadHandler) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + ClusterInfo * clusterInfo = apReadHandler->GetCluterInfolist(); + AttributeDataList::Builder attributeDataList = reportDataBuilder.CreateAttributeDataListBuilder(); + SuccessOrExit(reportDataBuilder.GetError()); + // TODO: Need to handle multiple chunk of message + while (clusterInfo != nullptr) + { + if (clusterInfo->IsDirty()) + { + AttributeDataElement::Builder attributeDataElementBuilder = attributeDataList.CreateAttributeDataElementBuilder(); + ChipLogDetail(DataManagement, " Cluster %u, Field %u is dirty", clusterInfo->mAttributePathParams.mClusterId, + clusterInfo->mAttributePathParams.mFieldId); + // Retrieve data for this cluster instance and clear its dirty flag. + err = RetrieveClusterData(attributeDataElementBuilder, *clusterInfo); + VerifyOrExit(err == CHIP_NO_ERROR, + ChipLogError(DataManagement, " Error retrieving data from cluster, aborting")); + } + + clusterInfo = clusterInfo->mpNext; + } + +exit: + ChipLogFunctError(err); + return err; +} + CHIP_ERROR Engine::BuildAndSendSingleReportData(ReadHandler * apReadHandler) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -52,6 +115,8 @@ CHIP_ERROR Engine::BuildAndSendSingleReportData(ReadHandler * apReadHandler) err = reportDataBuilder.Init(&reportDataWriter); SuccessOrExit(err); + err = BuildSingleReportDataAttributeDataList(reportDataBuilder, apReadHandler); + SuccessOrExit(err); // TODO: Fill in the EventList. // err = BuildSingleReportDataEventList(reportDataBuilder, apReadHandler); // SuccessOrExit(err); diff --git a/src/app/reporting/Engine.h b/src/app/reporting/Engine.h index f0300020140d97..9c41172e5e2c25 100644 --- a/src/app/reporting/Engine.h +++ b/src/app/reporting/Engine.h @@ -78,6 +78,9 @@ class Engine */ CHIP_ERROR BuildAndSendSingleReportData(ReadHandler * apReadHandler); + CHIP_ERROR BuildSingleReportDataAttributeDataList(ReportData::Builder & reportDataBuilder, ReadHandler * apReadHandler); + + CHIP_ERROR RetrieveClusterData(AttributeDataElement::Builder & aAttributeDataElementBuilder, ClusterInfo & aClusterInfo); /** * Send Report via ReadHandler * diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 64f775e99f3928..1e3908170d6ae0 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -27,6 +27,7 @@ chip_test_suite("tests") { "TestCommandInteraction.cpp", "TestCommandPathParams.cpp", "TestEventPathParams.cpp", + "TestInteractionModelEngine.cpp", "TestMessageDef.cpp", "TestReadInteraction.cpp", "TestReportingEngine.cpp", diff --git a/src/app/tests/TestClusterInfo.cpp b/src/app/tests/TestClusterInfo.cpp index b77e9c962506b0..0fcca3490c36ae 100644 --- a/src/app/tests/TestClusterInfo.cpp +++ b/src/app/tests/TestClusterInfo.cpp @@ -63,7 +63,7 @@ void TestDirty(nlTestSuite * apSuite, void * apContext) namespace { const nlTest sTests[] = { NL_TEST_DEF("TestSamePath", chip::app::TestClusterInfo::TestSamePath), NL_TEST_DEF("TestDifferentPath", chip::app::TestClusterInfo::TestDifferentPath), - NL_TEST_DEF("TestDirtiness", chip::app::TestClusterInfo::TestDirty), NL_TEST_SENTINEL() }; + NL_TEST_DEF("TestDirty", chip::app::TestClusterInfo::TestDirty), NL_TEST_SENTINEL() }; } int TestClusterInfo() diff --git a/src/app/tests/TestInteractionModelEngine.cpp b/src/app/tests/TestInteractionModelEngine.cpp new file mode 100644 index 00000000000000..098bf6330bb7c4 --- /dev/null +++ b/src/app/tests/TestInteractionModelEngine.cpp @@ -0,0 +1,151 @@ +/* + * + * 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 implements unit tests for CHIP Interaction Model Engine + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { +static chip::System::Layer gSystemLayer; +static chip::SecureSessionMgr gSessionManager; +static chip::Messaging::ExchangeManager gExchangeManager; +static chip::TransportMgr gTransportManager; +static const chip::Transport::AdminId gAdminId = 0; +} // namespace + +namespace chip { +namespace app { +class TestInteractionModelEngine +{ +public: + static void TestClusterInfoPushRelease(nlTestSuite * apSuite, void * apContext); + static int GetClusterInfoListLength(ClusterInfo * apClusterInfoList); +}; + +int TestInteractionModelEngine::GetClusterInfoListLength(ClusterInfo * apClusterInfoList) +{ + int length = 0; + ClusterInfo * runner = apClusterInfoList; + while (runner != nullptr) + { + runner = runner->mpNext; + length++; + } + return length; +} + +void TestInteractionModelEngine::TestClusterInfoPushRelease(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + err = InteractionModelEngine::GetInstance()->Init(&gExchangeManager, nullptr); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + ClusterInfo * clusterInfoList = nullptr; + AttributePathParams attributePathParams1(1, 2, 3, 4, 5, AttributePathFlags::kFieldIdValid); + AttributePathParams attributePathParams2(2, 3, 4, 5, 6, AttributePathFlags::kFieldIdValid); + AttributePathParams attributePathParams3(3, 4, 5, 6, 7, AttributePathFlags::kFieldIdValid); + + InteractionModelEngine::GetInstance()->PushFront(clusterInfoList, attributePathParams1); + NL_TEST_ASSERT(apSuite, clusterInfoList != nullptr && clusterInfoList->mAttributePathParams.IsSamePath(attributePathParams1)); + NL_TEST_ASSERT(apSuite, GetClusterInfoListLength(clusterInfoList) == 1); + + InteractionModelEngine::GetInstance()->PushFront(clusterInfoList, attributePathParams2); + NL_TEST_ASSERT(apSuite, clusterInfoList != nullptr && clusterInfoList->mAttributePathParams.IsSamePath(attributePathParams2)); + NL_TEST_ASSERT(apSuite, GetClusterInfoListLength(clusterInfoList) == 2); + + InteractionModelEngine::GetInstance()->PushFront(clusterInfoList, attributePathParams3); + NL_TEST_ASSERT(apSuite, clusterInfoList != nullptr && clusterInfoList->mAttributePathParams.IsSamePath(attributePathParams3)); + NL_TEST_ASSERT(apSuite, GetClusterInfoListLength(clusterInfoList) == 3); + + InteractionModelEngine::GetInstance()->ReleaseClusterInfoList(clusterInfoList); + NL_TEST_ASSERT(apSuite, GetClusterInfoListLength(clusterInfoList) == 0); +} +} // namespace app +} // namespace chip + +namespace { +void InitializeChip(nlTestSuite * apSuite) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::Optional peer(chip::Transport::Type::kUndefined); + chip::Transport::AdminPairingTable admins; + chip::Transport::AdminPairingInfo * adminInfo = admins.AssignAdminId(gAdminId, chip::kTestDeviceNodeId); + + NL_TEST_ASSERT(apSuite, adminInfo != nullptr); + + err = chip::Platform::MemoryInit(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + gSystemLayer.Init(nullptr); + + err = gSessionManager.Init(chip::kTestDeviceNodeId, &gSystemLayer, &gTransportManager, &admins); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + err = gExchangeManager.Init(&gSessionManager); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); +} + +// clang-format off +const nlTest sTests[] = + { + NL_TEST_DEF("TestClusterInfoPushRelease", chip::app::TestInteractionModelEngine::TestClusterInfoPushRelease), + NL_TEST_SENTINEL() + }; +// clang-format on +} // namespace + +int TestInteractionModelEngine() +{ + // clang-format off + nlTestSuite theSuite = + { + "TestInteractionModelEngine", + &sTests[0], + nullptr, + nullptr + }; + // clang-format on + + InitializeChip(&theSuite); + + nlTestRunner(&theSuite, nullptr); + + return (nlTestRunnerStats(&theSuite)); +} + +CHIP_REGISTER_TEST_SUITE(TestInteractionModelEngine) diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp index e7df1641914a9a..4df7aa083a7bbf 100644 --- a/src/app/tests/TestReadInteraction.cpp +++ b/src/app/tests/TestReadInteraction.cpp @@ -93,7 +93,8 @@ void TestReadInteraction::TestReadClient(nlTestSuite * apSuite, void * apContext err = readClient.Init(&gExchangeManager, nullptr); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); - err = readClient.SendReadRequest(kTestDeviceNodeId, gAdminId, nullptr, 0); + err = readClient.SendReadRequest(kTestDeviceNodeId, gAdminId, nullptr /*apEventPathParamsList*/, 0 /*aEventPathParamsListSize*/, + nullptr /*apAttributePathParamsList*/, 0 /*aAttributePathParamsListSize*/); NL_TEST_ASSERT(apSuite, err == CHIP_ERROR_INCORRECT_STATE); GenerateReportData(apSuite, apContext, buf); diff --git a/src/app/tests/TestReportingEngine.cpp b/src/app/tests/TestReportingEngine.cpp index 974bfaae367316..75a3e356521cea 100644 --- a/src/app/tests/TestReportingEngine.cpp +++ b/src/app/tests/TestReportingEngine.cpp @@ -48,8 +48,36 @@ static SecureSessionMgr gSessionManager; static Messaging::ExchangeManager gExchangeManager; static TransportMgr gTransportManager; static const Transport::AdminId gAdminId = 0; +constexpr ClusterId kTestClusterId = 6; +constexpr EndpointId kTestEndpointId = 1; +constexpr chip::FieldId kTestFieldId1 = 1; +constexpr chip::FieldId kTestFieldId2 = 2; +constexpr uint8_t kTestFieldValue1 = 1; +constexpr uint8_t kTestFieldValue2 = 2; namespace app { +CHIP_ERROR ReadSingleClusterData(AttributePathParams & aAttributePathParams, TLV::TLVWriter & aWriter) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + VerifyOrExit(aAttributePathParams.mClusterId == kTestClusterId && aAttributePathParams.mEndpointId == kTestEndpointId, + err = CHIP_ERROR_INVALID_ARGUMENT); + + if (aAttributePathParams.mFieldId == kRootFieldId || aAttributePathParams.mFieldId == kTestFieldId1) + { + err = aWriter.Put(TLV::ContextTag(kTestFieldId1), kTestFieldValue1); + SuccessOrExit(err); + } + if (aAttributePathParams.mFieldId == kRootFieldId || aAttributePathParams.mFieldId == kTestFieldId2) + { + err = aWriter.Put(TLV::ContextTag(kTestFieldId2), kTestFieldValue2); + SuccessOrExit(err); + } + +exit: + ChipLogFunctError(err); + return err; +} + namespace reporting { class TestReportingEngine { @@ -74,6 +102,8 @@ void TestReportingEngine::TestBuildAndSendSingleReportData(nlTestSuite * apSuite System::PacketBufferTLVWriter writer; System::PacketBufferHandle readRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); ReadRequest::Builder readRequestBuilder; + AttributePathList::Builder attributePathListBuilder; + AttributePath::Builder attributePathBuilder; err = InteractionModelEngine::GetInstance()->Init(&gExchangeManager, nullptr); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); @@ -84,6 +114,14 @@ void TestReportingEngine::TestBuildAndSendSingleReportData(nlTestSuite * apSuite writer.Init(std::move(readRequestbuf)); err = readRequestBuilder.Init(&writer); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + attributePathListBuilder = readRequestBuilder.CreateAttributePathListBuilder(); + NL_TEST_ASSERT(apSuite, readRequestBuilder.GetError() == CHIP_NO_ERROR); + attributePathBuilder = attributePathListBuilder.CreateAttributePathBuilder(); + NL_TEST_ASSERT(apSuite, attributePathListBuilder.GetError() == CHIP_NO_ERROR); + attributePathBuilder = + attributePathBuilder.NodeId(1).EndpointId(kTestEndpointId).ClusterId(kTestClusterId).FieldId(0).EndOfAttributePath(); + NL_TEST_ASSERT(apSuite, attributePathBuilder.GetError() == CHIP_NO_ERROR); + attributePathListBuilder.EndOfAttributePathList(); readRequestBuilder.EventNumber(1); NL_TEST_ASSERT(apSuite, readRequestBuilder.GetError() == CHIP_NO_ERROR); readRequestBuilder.EndOfReadRequest(); diff --git a/src/app/tests/integration/chip_im_initiator.cpp b/src/app/tests/integration/chip_im_initiator.cpp index 8f53eae8217de1..52e3f787d6a6f1 100644 --- a/src/app/tests/integration/chip_im_initiator.cpp +++ b/src/app/tests/integration/chip_im_initiator.cpp @@ -86,7 +86,7 @@ CHIP_ERROR SendCommandRequest(void) printf("\nSend invoke command request message to Node: %" PRIu64 "\n", chip::kTestDeviceNodeId); - chip::app::CommandPathParams commandPathParams = { kTestEndPointId, // Endpoint + chip::app::CommandPathParams commandPathParams = { kTestEndpointId, // Endpoint kTestGroupId, // GroupId kTestClusterId, // ClusterId kTestCommandId, // CommandId @@ -129,12 +129,12 @@ CHIP_ERROR SendCommandRequest(void) CHIP_ERROR SendReadRequest(void) { CHIP_ERROR err = CHIP_NO_ERROR; - - gLastMessageTime = chip::System::Timer::GetCurrentEpoch(); + chip::app::AttributePathParams attributePathParams(chip::kTestDeviceNodeId, kTestEndpointId, kTestClusterId, 1, 0, + chip::app::AttributePathFlags::kFieldIdValid); printf("\nSend read request message to Node: %" PRIu64 "\n", chip::kTestDeviceNodeId); - err = gpReadClient->SendReadRequest(chip::kTestDeviceNodeId, gAdminId, nullptr, 0); + err = gpReadClient->SendReadRequest(chip::kTestDeviceNodeId, gAdminId, nullptr, 0, &attributePathParams, 1); SuccessOrExit(err); if (err == CHIP_NO_ERROR) @@ -254,7 +254,7 @@ namespace app { void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aCommandId, chip::EndpointId aEndPointId, chip::TLV::TLVReader & aReader, Command * apCommandObj) { - if (aClusterId != kTestClusterId || aCommandId != kTestCommandId || aEndPointId != kTestEndPointId) + if (aClusterId != kTestClusterId || aCommandId != kTestCommandId || aEndPointId != kTestEndpointId) { return; } @@ -265,6 +265,19 @@ void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aC } } +CHIP_ERROR WriteSingleClusterData(AttributePathParams & aAttributePathParams, TLV::TLVReader & aReader) +{ + if (aAttributePathParams.mClusterId != kTestClusterId || aAttributePathParams.mEndpointId != kTestEndpointId) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + if (aReader.GetLength() != 0) + { + chip::TLV::Debug::Dump(aReader, TLVPrettyPrinter); + } + return CHIP_NO_ERROR; +} } // namespace app } // namespace chip diff --git a/src/app/tests/integration/chip_im_responder.cpp b/src/app/tests/integration/chip_im_responder.cpp index cd90329b96a6f9..9ad3f40e8cfd67 100644 --- a/src/app/tests/integration/chip_im_responder.cpp +++ b/src/app/tests/integration/chip_im_responder.cpp @@ -47,7 +47,7 @@ void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aC CHIP_ERROR err = CHIP_NO_ERROR; static bool statusCodeFlipper = false; - if (aClusterId != kTestClusterId || aCommandId != kTestCommandId || aEndPointId != kTestEndPointId) + if (aClusterId != kTestClusterId || aCommandId != kTestCommandId || aEndPointId != kTestEndpointId) { return; } @@ -57,17 +57,13 @@ void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aC chip::TLV::Debug::Dump(aReader, TLVPrettyPrinter); } - chip::app::CommandPathParams commandPathParams = { kTestEndPointId, // Endpoint + chip::app::CommandPathParams commandPathParams = { kTestEndpointId, // Endpoint kTestGroupId, // GroupId kTestClusterId, // ClusterId kTestCommandId, // CommandId (chip::app::CommandPathFlags::kEndpointIdValid) }; // Add command data here - - uint8_t effectIdentifier = 1; // Dying light - uint8_t effectVariant = 1; - if (statusCodeFlipper) { printf("responder constructing status code in command"); @@ -84,10 +80,10 @@ void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aC SuccessOrExit(err); writer = apCommandObj->GetCommandDataElementTLVWriter(); - err = writer->Put(chip::TLV::ContextTag(1), effectIdentifier); + err = writer->Put(chip::TLV::ContextTag(kTestFieldId1), kTestFieldValue1); SuccessOrExit(err); - err = writer->Put(chip::TLV::ContextTag(2), effectVariant); + err = writer->Put(chip::TLV::ContextTag(kTestFieldId2), kTestFieldValue2); SuccessOrExit(err); err = apCommandObj->FinishCommand(); @@ -99,6 +95,27 @@ void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aC return; } +CHIP_ERROR ReadSingleClusterData(AttributePathParams & aAttributePathParams, TLV::TLVWriter & aWriter) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + VerifyOrExit(aAttributePathParams.mClusterId == kTestClusterId && aAttributePathParams.mEndpointId == kTestEndpointId, + err = CHIP_ERROR_INVALID_ARGUMENT); + + if (aAttributePathParams.mFieldId == kRootFieldId || aAttributePathParams.mFieldId == 1) + { + err = aWriter.Put(TLV::ContextTag(kTestFieldId1), kTestFieldValue1); + SuccessOrExit(err); + } + if (aAttributePathParams.mFieldId == kRootFieldId || aAttributePathParams.mFieldId == 2) + { + err = aWriter.Put(TLV::ContextTag(kTestFieldId2), kTestFieldValue2); + SuccessOrExit(err); + } + +exit: + ChipLogFunctError(err); + return err; +} } // namespace app } // namespace chip diff --git a/src/app/tests/integration/common.h b/src/app/tests/integration/common.h index e3646dbc7250a5..2b69ebf07e2565 100644 --- a/src/app/tests/integration/common.h +++ b/src/app/tests/integration/common.h @@ -34,9 +34,12 @@ extern chip::Messaging::ExchangeManager gExchangeManager; constexpr chip::ClusterId kTestClusterId = 6; constexpr chip::CommandId kTestCommandId = 40; -constexpr chip::EndpointId kTestEndPointId = 1; +constexpr chip::EndpointId kTestEndpointId = 1; constexpr chip::GroupId kTestGroupId = 0; - +constexpr chip::FieldId kTestFieldId1 = 1; +constexpr chip::FieldId kTestFieldId2 = 2; +constexpr uint8_t kTestFieldValue1 = 1; +constexpr uint8_t kTestFieldValue2 = 2; void InitializeChip(void); void ShutdownChip(void); void TLVPrettyPrinter(const char * aFormat, ...);