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, ...);