From 4fb830a770b09f07d4a87dd6f03ef59cf2285b24 Mon Sep 17 00:00:00 2001 From: yunhanw Date: Wed, 21 Apr 2021 12:38:45 -0700 Subject: [PATCH] Add IM/Ember Read/Writer Interface and attribute read for IM 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 | 1 + src/app/InteractionModelEngine.cpp | 84 +++++++++++ src/app/InteractionModelEngine.h | 16 ++- src/app/ReadClient.cpp | 133 ++++++++++++++---- src/app/ReadClient.h | 7 +- src/app/ReadHandler.cpp | 95 +++++++++++++ src/app/ReadHandler.h | 19 ++- src/app/reporting/Engine.cpp | 68 +++++++++ src/app/reporting/Engine.h | 3 + src/app/tests/TestReadInteraction.cpp | 2 +- src/app/tests/TestReportingEngine.cpp | 38 +++++ .../tests/integration/chip_im_initiator.cpp | 20 ++- .../tests/integration/chip_im_responder.cpp | 29 +++- src/app/tests/integration/common.h | 5 +- 15 files changed, 480 insertions(+), 50 deletions(-) diff --git a/src/app/AttributePathParams.h b/src/app/AttributePathParams.h index cfde12a9899ccc..3655722169247e 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..ef21d09b7abb78 100644 --- a/src/app/ClusterInfo.h +++ b/src/app/ClusterInfo.h @@ -28,6 +28,7 @@ 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; } diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index e243b7a621e2b4..0846d2bc7ebb12 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -227,6 +227,56 @@ void InteractionModelEngine::OnResponseTimeout(Messaging::ExchangeContext * ec) ChipLogProgress(DataManagement, "Time out! failed to receive echo response from Exchange: %d", ec->GetExchangeId()); } +void InteractionModelEngine::ReleaseClusterInfoListToPool(ReadHandler * const apReadHandler) +{ + ClusterInfo * const clusterInfoList = apReadHandler->GetClusterInfoList(); + long numClusterInfos = apReadHandler->GetNumClusterInfos(); + long numClusterInfosToBeAffected; + + if (numClusterInfos == 0) + { + ChipLogDetail(DataManagement, "No cluster instances allocated"); + return; + } + + apReadHandler->ClearClusterInfo(); + + // make sure everything is still sane + ChipLogIfFalse(clusterInfoList >= mClusterInfoPool); + ChipLogIfFalse(numClusterInfos <= mNumClusterInfos); + + // mClusterInfoPool + mNumClusterInfos is a pointer which points to the last+1byte of this array + // clusterInfoList is a pointer to the first cluster instance to be released + // the result of subtraction is the number of cluster instances from clusterInfoList to the end of this array + numClusterInfosToBeAffected = &mClusterInfoPool[mNumClusterInfos] - clusterInfoList; + + // Shrink the clusterInfosInPool by the number of cluster instances. + mNumClusterInfos -= numClusterInfos; + + ChipLogDetail(DataManagement, "numClusterInfos is %d, and numClusterInfosToBeAffected is %l", numClusterInfos, + numClusterInfosToBeAffected); + if (numClusterInfos == numClusterInfosToBeAffected) + { + ChipLogDetail(DataManagement, "Releasing the last block of cluster instances"); + return; + } + + ChipLogDetail(DataManagement, "Moving %d cluster infos forward", numClusterInfosToBeAffected - numClusterInfos); + + memmove(clusterInfoList, clusterInfoList + numClusterInfos, + sizeof(ClusterInfo) * static_cast(numClusterInfosToBeAffected - numClusterInfos)); + + for (size_t i = 0; i < CHIP_MAX_NUM_READ_HANDLER; ++i) + { + ReadHandler * const handler = mReadHandlers + i; + + if ((apReadHandler != handler) && (handler->GetClusterInfoList() > clusterInfoList)) + { + handler->ShrinkClusterInfo(numClusterInfos); + } + } +} + // The default implementation to make compiler happy before codegen for this is ready. // TODO: Remove this after codegen is ready. void __attribute__((weak)) @@ -240,9 +290,43 @@ 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(NodeId aNodeId, ClusterId aClusterId, EndpointId aEndPointId, FieldId aFieldId, TLV::TLVWriter & aWriter) +{ + ChipLogDetail(DataManagement, + "Received Cluster Command: Cluster=%" PRIx16 " NodeId=%" PRIx64 " Endpoint=%" PRIx8 " FieldId=%" PRIx8, + aClusterId, aNodeId, aEndPointId, aFieldId); + ChipLogError(DataManagement, + "Default ReadSingleClusterData is called, this should be replaced by actual dispatched for cluster"); + return CHIP_NO_ERROR; +} + +CHIP_ERROR __attribute__((weak)) +WriteSingleClusterData(NodeId aNodeId, ClusterId aClusterId, EndpointId aEndPointId, FieldId aFieldId, TLV::TLVReader & aReader) +{ + ChipLogDetail(DataManagement, + "Received Cluster Command: Cluster=%" PRIx16 " NodeId=%" PRIx64 " Endpoint=%" PRIx8 " FieldId=%" PRIx8, + aClusterId, aNodeId, aEndPointId, aFieldId); + 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); } + +CHIP_ERROR InteractionModelEngine::GetFirstAvailableClusterInfo(ClusterInfo *& apClusterInfo) +{ + if (mNumClusterInfos >= IM_SERVER_MAX_NUM_PATH_GROUPS) + { + return CHIP_ERROR_NO_MEMORY; + } + apClusterInfo = &mClusterInfoPool[mNumClusterInfos]; + mNumClusterInfos++; + apClusterInfo->ClearDirty(); + return CHIP_NO_ERROR; +} } // namespace app } // namespace chip diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index 820789e3a669b4..cc056fa66cce32 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,10 @@ class InteractionModelEngine : public Messaging::ExchangeDelegate reporting::Engine & GetReportingEngine() { return mReportingEngine; } + void ReleaseClusterInfoListToPool(ReadHandler * const apReadHandler); + + CHIP_ERROR GetFirstAvailableClusterInfo(ClusterInfo *& apClusterInfo); + private: friend class reporting::Engine; void OnUnknownMsgType(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, @@ -151,10 +158,15 @@ class InteractionModelEngine : public Messaging::ExchangeDelegate ReadClient mReadClients[CHIP_MAX_NUM_READ_CLIENT]; ReadHandler mReadHandlers[CHIP_MAX_NUM_READ_HANDLER]; reporting::Engine mReportingEngine; + long mNumClusterInfos = 0; + ClusterInfo mClusterInfoPool[IM_SERVER_MAX_NUM_PATH_GROUPS]; }; void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aCommandId, chip::EndpointId aEndPointId, chip::TLV::TLVReader & aReader, Command * apCommandObj); - +CHIP_ERROR ReadSingleClusterData(NodeId aNodeId, ClusterId aClusterId, EndpointId aEndPointId, FieldId aFieldId, + TLV::TLVWriter & aWriter); +CHIP_ERROR WriteSingleClusterData(NodeId aNodeId, ClusterId aClusterId, EndpointId aEndPointId, FieldId aFieldId, + TLV::TLVReader & aReader); } // namespace app } // namespace chip diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp index c4fd9583a772ed..cf19d471b765d3 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,52 @@ 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())) + { + NodeId nodeId = 0; + EndpointId endpointId = 0; + ClusterId clusterId = 0; + FieldId fieldId = 0; + chip::TLV::TLVReader dataReader; + AttributeDataElement::Parser element; + AttributePath::Parser attributePathParser; + TLV::TLVReader reader = aAttributeDataListReader; + err = element.Init(reader); + SuccessOrExit(err); + + err = element.GetAttributePath(&attributePathParser); + SuccessOrExit(err); + + err = attributePathParser.GetNodeId(&nodeId); + SuccessOrExit(err); + + err = attributePathParser.GetEndpointId(&endpointId); + SuccessOrExit(err); + + err = attributePathParser.GetClusterId(&clusterId); + SuccessOrExit(err); + + err = attributePathParser.GetFieldId(&fieldId); + SuccessOrExit(err); + + err = element.GetData(&dataReader); + SuccessOrExit(err); + err = WriteSingleClusterData(nodeId, clusterId, endpointId, fieldId, 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..04890bf98621a5 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; + ClearClusterInfo(); MoveToState(HandlerState::Initialized); exit: @@ -48,9 +49,11 @@ CHIP_ERROR ReadHandler::Init(InteractionModelDelegate * apDelegate) void ReadHandler::Shutdown() { + InteractionModelEngine::GetInstance()->ReleaseClusterInfoListToPool(this); ClearExistingExchangeContext(); MoveToState(HandlerState::Uninitialized); mpDelegate = nullptr; + ClearClusterInfo(); } CHIP_ERROR ReadHandler::ClearExistingExchangeContext() @@ -92,6 +95,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 +107,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 +122,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 +171,74 @@ 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; + NodeId nodeId = 0; + EndpointId endpointId = 0; + ClusterId clusterId = 0; + FieldId fieldId = 0; + ClusterInfo * clusterInfo = nullptr; + err = path.Init(reader); + SuccessOrExit(err); + err = path.GetNodeId(&nodeId); + SuccessOrExit(err); + err = path.GetEndpointId(&endpointId); + SuccessOrExit(err); + err = path.GetClusterId(&clusterId); + SuccessOrExit(err); + err = path.GetFieldId(&fieldId); + SuccessOrExit(err); + attributePathParams.mNodeId = nodeId; + attributePathParams.mEndpointId = endpointId; + attributePathParams.mClusterId = clusterId; + attributePathParams.mFieldId = fieldId; + + for (long i = 0; i < mNumClusterInfos; ++i) + { + if (mpClusterInfoList[i].mAttributePathParams.IsSamePath(attributePathParams)) + { + clusterInfo = &mpClusterInfoList[i]; + break; + } + } + if (clusterInfo == nullptr) + { + err = InteractionModelEngine::GetInstance()->GetFirstAvailableClusterInfo(clusterInfo); + SuccessOrExit(err); + ++mNumClusterInfos; + } + clusterInfo->mAttributePathParams = attributePathParams; + if (nullptr == mpClusterInfoList) + { + // this the first cluster instance for this read handler + // mNumClusterInfoList has already be incremented + mpClusterInfoList = clusterInfo; + } + clusterInfo->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 @@ -177,5 +263,14 @@ void ReadHandler::MoveToState(const HandlerState aTargetState) ChipLogDetail(DataManagement, "IM RH moving to [%s]", GetStateStr()); } +CHIP_ERROR ReadHandler::GetProcessingClusterInfo(ClusterInfo *& apClusterInfo) +{ + if (mpClusterInfoList == nullptr) + { + return CHIP_ERROR_INCORRECT_STATE; + } + apClusterInfo = &mpClusterInfoList[mCurProcessingClusterInfoIdx]; + return CHIP_NO_ERROR; +} } // namespace app } // namespace chip diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 9c7a5e60286a1c..678fd43e2810eb 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -24,6 +24,7 @@ #pragma once +#include #include #include #include @@ -99,6 +100,19 @@ class ReadHandler virtual ~ReadHandler() = default; + CHIP_ERROR GetProcessingClusterInfo(ClusterInfo *& apClusterInfo); + ClusterInfo * GetClusterInfoList() { return mpClusterInfoList; }; + long GetNumClusterInfos() { return mNumClusterInfos; } + long GetProcessingClusterIndex() { return mCurProcessingClusterInfoIdx; }; + void BumpProcessingClusterIndex() { mCurProcessingClusterInfoIdx++; }; + void ShrinkClusterInfo(long numClusterInfos) { mpClusterInfoList -= numClusterInfos; } + void ClearClusterInfo() + { + mpClusterInfoList = nullptr; + mCurProcessingClusterInfoIdx = 0; + mNumClusterInfos = 0; + } + private: enum class HandlerState { @@ -108,7 +122,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 +139,9 @@ class ReadHandler // Current Handler state HandlerState mState; + ClusterInfo * mpClusterInfoList = nullptr; + long mCurProcessingClusterInfoIdx = 0; + long mNumClusterInfos = 0; }; } // namespace app } // namespace chip diff --git a/src/app/reporting/Engine.cpp b/src/app/reporting/Engine.cpp index e63530628a932c..a8d4aa76a8a480 100644 --- a/src/app/reporting/Engine.cpp +++ b/src/app/reporting/Engine.cpp @@ -37,6 +37,72 @@ 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.mNodeId, aClusterInfo.mAttributePathParams.mClusterId, + aClusterInfo.mAttributePathParams.mEndpointId, aClusterInfo.mAttributePathParams.mFieldId, + *(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; + AttributeDataList::Builder attributeDataList = reportDataBuilder.CreateAttributeDataListBuilder(); + SuccessOrExit(reportDataBuilder.GetError()); + // TODO: Need to handle multiple chunk of message + while (apReadHandler->GetProcessingClusterIndex() < apReadHandler->GetNumClusterInfos()) + { + ClusterInfo * clusterInfo = nullptr; + err = apReadHandler->GetProcessingClusterInfo(clusterInfo); + SuccessOrExit(err); + if (clusterInfo->IsDirty()) + { + AttributeDataElement::Builder attributeDataElementBuilder = attributeDataList.CreateAttributeDataElementBuilder(); + ChipLogDetail(DataManagement, " Cluster %u is dirty", apReadHandler->GetProcessingClusterIndex()); + // 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")); + } + + apReadHandler->BumpProcessingClusterIndex(); + } + +exit: + ChipLogFunctError(err); + return err; +} + CHIP_ERROR Engine::BuildAndSendSingleReportData(ReadHandler * apReadHandler) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -52,6 +118,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 cf47c4b9674408..0496d2fa729579 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/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp index e7df1641914a9a..dfae4a92e70c16 100644 --- a/src/app/tests/TestReadInteraction.cpp +++ b/src/app/tests/TestReadInteraction.cpp @@ -93,7 +93,7 @@ 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, 0, nullptr, 0); 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..01d1f67c901c08 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(NodeId aNodeId, ClusterId aClusterId, EndpointId aEndPointId, FieldId aFieldId, + TLV::TLVWriter & aWriter) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + VerifyOrExit(aClusterId == kTestClusterId && aEndPointId == kTestEndPointId, err = CHIP_ERROR_INVALID_ARGUMENT); + + if (aFieldId == kRootFieldId || aFieldId == kTestFieldId1) + { + err = aWriter.Put(TLV::ContextTag(kTestFieldId1), kTestFieldValue1); + SuccessOrExit(err); + } + if (aFieldId == kRootFieldId || aFieldId == 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..346932a6fb5c94 100644 --- a/src/app/tests/integration/chip_im_initiator.cpp +++ b/src/app/tests/integration/chip_im_initiator.cpp @@ -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) @@ -265,6 +265,20 @@ void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aC } } +CHIP_ERROR WriteSingleClusterData(NodeId aNodeId, ClusterId aClusterId, EndpointId aEndPointId, FieldId aFieldId, + TLV::TLVReader & aReader) +{ + if (aClusterId != kTestClusterId || aEndPointId != 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..d48183c7311575 100644 --- a/src/app/tests/integration/chip_im_responder.cpp +++ b/src/app/tests/integration/chip_im_responder.cpp @@ -64,10 +64,6 @@ void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aC (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(NodeId aNodeId, ClusterId aClusterId, EndpointId aEndPointId, FieldId aFieldId, + TLV::TLVWriter & aWriter) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + VerifyOrExit(aClusterId == kTestClusterId && aEndPointId == kTestEndPointId, err = CHIP_ERROR_INVALID_ARGUMENT); + + if (aFieldId == kRootFieldId || aFieldId == 1) + { + err = aWriter.Put(TLV::ContextTag(kTestFieldId1), kTestFieldValue1); + SuccessOrExit(err); + } + if (aFieldId == kRootFieldId || aFieldId == 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..ba8cace5f7ae4e 100644 --- a/src/app/tests/integration/common.h +++ b/src/app/tests/integration/common.h @@ -36,7 +36,10 @@ constexpr chip::ClusterId kTestClusterId = 6; constexpr chip::CommandId kTestCommandId = 40; 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, ...);