diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index b43ad7e8efdf34..63556e58f72c78 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -78,6 +78,8 @@ static_library("app") { "MessageDef/ReportData.h", "MessageDef/StatusElement.cpp", "MessageDef/StatusElement.h", + "ReadClient.cpp", + "ReadHandler.cpp", "decoder.cpp", "encoder.cpp", ] diff --git a/src/app/CommandHandler.cpp b/src/app/CommandHandler.cpp index 8554340dd707ae..0c72531b6228ec 100644 --- a/src/app/CommandHandler.cpp +++ b/src/app/CommandHandler.cpp @@ -58,7 +58,7 @@ CHIP_ERROR CommandHandler::SendCommandResponse() err = FinalizeCommandsMessage(); SuccessOrExit(err); - VerifyOrExit(mpExchangeCtx != NULL, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mpExchangeCtx != nullptr, err = CHIP_ERROR_INCORRECT_STATE); err = mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::InvokeCommandResponse, std::move(mCommandMessageBuf), Messaging::SendFlags(Messaging::SendMessageFlags::kNone)); SuccessOrExit(err); diff --git a/src/app/CommandSender.cpp b/src/app/CommandSender.cpp index d3dcda683f2a97..e81138da63e627 100644 --- a/src/app/CommandSender.cpp +++ b/src/app/CommandSender.cpp @@ -92,7 +92,6 @@ void CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExchangeCon exit: Reset(); - return; } void CommandSender::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) diff --git a/src/app/InteractionModelDelegate.h b/src/app/InteractionModelDelegate.h new file mode 100644 index 00000000000000..216912ba2962bd --- /dev/null +++ b/src/app/InteractionModelDelegate.h @@ -0,0 +1,118 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines the classes corresponding to CHIP Interaction Model Delegate. + * + */ + +#pragma once + +#include +#include +#include + +namespace chip { +namespace app { + +struct EventPathParams; + +/** + * @brief + * This class provides a skeleton for the callback functions. The functions will be + * called by InteractionModel on specific CallbackId. If the user of InteractionModel + * is interested in receiving these callbacks, they can specialize this class and handle + * each CallbackId in their implementation of this class. + */ +class InteractionModelDelegate +{ +public: + virtual ~InteractionModelDelegate() {} + + // Interaction Model would use the below callbackID as discriminator and notify the zcl/app the particular interaction + // has completed or is happening or need more things from upper layer via InParam and OutParam + // The below callbacks may or may not be necessary for upper zcl, once the zcl integration is complete, we will revisit these + enum class CallbackId + { + // Read Client + /** + * Sent when it is ready to send the ReadRequest. The + * application is expected to fill in the attribute/event paths that it wants to + * read/subscribe to. + */ + kReadRequestPrepareNeeded, + // IM notify zcl events can be further processed in app. + kEventStreamReceived, + + // IM notify zcl all reports have been processed. + kReportProcessed, + }; + + /** + * Incoming parameters sent with callback generated directly from this component + * + */ + union InParam + { + InParam() { Clear(); } + + void Clear(void) { memset(this, 0, sizeof(*this)); } + + struct + { + // ExchangeContext managed externally and should not be closed by zcl processing. + Messaging::ExchangeContext * exchangeContext; + // EventList tlv reader, upper layer can process event list. + chip::TLV::TLVReader * reader; + } mEventStreamReceived; + }; + + /** + * Outgoing parameters sent with CallbackId generated directly from this component + * + */ + union OutParam + { + OutParam() { Clear(); } + void Clear(void) { memset(this, 0, sizeof(*this)); } + + struct + { + EventPathParams * eventPathParamsList; //< Pointer to a list of event path parameter ZCL are intereted in + size_t eventPathParamsListSize; //< Number of event paths in mpEventPathList + uint64_t eventNumber; //< A event number it has already to limit the set of retrieved events on the server for + // optimization purposes + } mReadRequestPrepareNeeded; + }; + + /** + * @brief Set the callback function + * @param[in] aCallbackId A function pointer for particular call back + * @param[in] aInParam A const reference to the input parameter for this CallbackId + * @param[out] aOutParam A reference to the output parameter for this CallbackId + */ + virtual void HandleIMCallBack(CallbackId aCallbackId, const InParam & aInParam, OutParam & aOutParam) = 0; + + void DefaultCallbackIdHandler(CallbackId aCallbackId, const InParam & aInParam, OutParam & aOutParam) + { + ChipLogDetail(DataManagement, "%s CallbackId: %d", __func__, aCallbackId); + } +}; + +} // namespace app +} // namespace chip diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 1df693f67354d1..26112d6c6bcf9a 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,12 +23,11 @@ * */ -#include - +#include "InteractionModelEngine.h" #include "Command.h" #include "CommandHandler.h" #include "CommandSender.h" -#include "InteractionModelEngine.h" +#include namespace chip { namespace app { @@ -41,29 +40,12 @@ InteractionModelEngine * InteractionModelEngine::GetInstance() return &sInteractionModelEngine; } -void InteractionModelEngine::SetEventCallback(void * apAppState, EventCallback aEventCallback) -{ - mpAppState = apAppState; - mEventCallback = aEventCallback; -} - -void InteractionModelEngine::DefaultEventHandler(EventID aEvent, const InEventParam & aInParam, OutEventParam & aOutParam) -{ - IgnoreUnusedVariable(aInParam); - IgnoreUnusedVariable(aOutParam); - - ChipLogDetail(DataManagement, "%s event: %d", __func__, aEvent); -} - -CHIP_ERROR InteractionModelEngine::Init(Messaging::ExchangeManager * apExchangeMgr) +CHIP_ERROR InteractionModelEngine::Init(Messaging::ExchangeManager * apExchangeMgr, InteractionModelDelegate * apDelegate) { CHIP_ERROR err = CHIP_NO_ERROR; - // Error if already initialized. - if (mpExchangeMgr != nullptr) - return CHIP_ERROR_INCORRECT_STATE; - mpExchangeMgr = apExchangeMgr; + mpDelegate = apDelegate; err = mpExchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::kProtocol_InteractionModel, this); SuccessOrExit(err); @@ -74,27 +56,42 @@ CHIP_ERROR InteractionModelEngine::Init(Messaging::ExchangeManager * apExchangeM void InteractionModelEngine::Shutdown() { - for (size_t i = 0; i < CHIP_MAX_NUM_COMMAND_HANDLER_OBJECTS; ++i) + for (auto & commandSender : mCommandSenderObjs) + { + commandSender.Shutdown(); + } + + for (auto & commandHandler : mCommandHandlerObjs) + { + commandHandler.Shutdown(); + } + + for (auto & readClient : mReadClients) + { + readClient.Shutdown(); + } + + for (auto & readHandler : mReadHandlers) { - mCommandHandlerObjs[i].Shutdown(); + readHandler.Shutdown(); } } -CHIP_ERROR InteractionModelEngine::NewCommandSender(CommandSender ** const apComandSender) +CHIP_ERROR InteractionModelEngine::NewCommandSender(CommandSender ** const apCommandSender) { - CHIP_ERROR err = CHIP_ERROR_NO_MEMORY; - *apComandSender = nullptr; + CHIP_ERROR err = CHIP_ERROR_NO_MEMORY; + *apCommandSender = nullptr; - for (size_t i = 0; i < CHIP_MAX_NUM_COMMAND_SENDER_OBJECTS; ++i) + for (auto & commandSender : mCommandSenderObjs) { - if (mCommandHandlerObjs[i].IsFree()) + if (commandSender.IsFree()) { - *apComandSender = &mCommandSenderObjs[i]; - err = mCommandSenderObjs[i].Init(mpExchangeMgr); + *apCommandSender = &commandSender; + err = commandSender.Init(mpExchangeMgr); SuccessOrExit(err); if (CHIP_NO_ERROR != err) { - *apComandSender = nullptr; + *apCommandSender = nullptr; ExitNow(); } break; @@ -105,7 +102,28 @@ CHIP_ERROR InteractionModelEngine::NewCommandSender(CommandSender ** const apCom return err; } -void InteractionModelEngine::OnUnknownMsgType(Messaging::ExchangeContext * apEc, const PacketHeader & aPacketHeader, +CHIP_ERROR InteractionModelEngine::NewReadClient(ReadClient ** const apReadClient, InteractionModelDelegate * apDelegate) +{ + CHIP_ERROR err = CHIP_ERROR_NO_MEMORY; + + for (auto & readClient : mReadClients) + { + if (readClient.IsFree()) + { + *apReadClient = &readClient; + err = readClient.Init(mpExchangeMgr, apDelegate); + if (CHIP_NO_ERROR != err) + { + *apReadClient = nullptr; + } + return err; + } + } + + return err; +} + +void InteractionModelEngine::OnUnknownMsgType(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -116,51 +134,61 @@ void InteractionModelEngine::OnUnknownMsgType(Messaging::ExchangeContext * apEc, // err = SendStatusReport(ec, kChipProfile_Common, kStatus_UnsupportedMessage); // SuccessOrExit(err); - apEc->Close(); - apEc = NULL; + apExchangeContext->Close(); + apExchangeContext = nullptr; ChipLogFunctError(err); - if (NULL != apEc) + if (nullptr != apExchangeContext) { - apEc->Abort(); - apEc = NULL; + apExchangeContext->Abort(); + apExchangeContext = nullptr; } } -void InteractionModelEngine::OnInvokeCommandRequest(Messaging::ExchangeContext * apEc, const PacketHeader & aPacketHeader, - const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload) +void InteractionModelEngine::OnInvokeCommandRequest(Messaging::ExchangeContext * apExchangeContext, + const PacketHeader & aPacketHeader, const PayloadHeader & aPayloadHeader, + System::PacketBufferHandle aPayload) { - CHIP_ERROR err = CHIP_NO_ERROR; - CommandHandler * commandServer = nullptr; + CHIP_ERROR err = CHIP_NO_ERROR; - if (nullptr != mEventCallback) + for (auto & commandHandler : mCommandHandlerObjs) { - InEventParam inParam; - OutEventParam outParam; - inParam.Clear(); - outParam.Clear(); - outParam.mIncomingInvokeCommandRequest.mShouldContinueProcessing = true; - inParam.mIncomingInvokeCommandRequest.mpPacketHeader = &aPacketHeader; - - mEventCallback(mpAppState, kEvent_OnIncomingInvokeCommandRequest, inParam, outParam); - - if (!outParam.mIncomingInvokeCommandRequest.mShouldContinueProcessing) + if (commandHandler.IsFree()) { - ChipLogDetail(DataManagement, "Command not allowed"); - ExitNow(); + err = commandHandler.Init(mpExchangeMgr); + SuccessOrExit(err); + commandHandler.OnMessageReceived(apExchangeContext, aPacketHeader, aPayloadHeader, std::move(aPayload)); + apExchangeContext = nullptr; + break; } } - for (size_t i = 0; i < CHIP_MAX_NUM_COMMAND_HANDLER_OBJECTS; ++i) +exit: + ChipLogFunctError(err); + + if (nullptr != apExchangeContext) + { + apExchangeContext->Abort(); + apExchangeContext = nullptr; + } +} + +void InteractionModelEngine::OnReadRequest(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, + const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + ChipLogDetail(DataManagement, "Receive Read request"); + + for (auto & readHandler : mReadHandlers) { - if (mCommandHandlerObjs[i].IsFree()) + if (readHandler.IsFree()) { - commandServer = &mCommandHandlerObjs[i]; - err = commandServer->Init(mpExchangeMgr); + err = readHandler.Init(mpExchangeMgr, mpDelegate); SuccessOrExit(err); - commandServer->OnMessageReceived(apEc, aPacketHeader, aPayloadHeader, std::move(aPayload)); - apEc = nullptr; + readHandler.OnMessageReceived(apExchangeContext, aPacketHeader, aPayloadHeader, std::move(aPayload)); + apExchangeContext = nullptr; break; } } @@ -168,23 +196,28 @@ void InteractionModelEngine::OnInvokeCommandRequest(Messaging::ExchangeContext * exit: ChipLogFunctError(err); - if (nullptr != apEc) + if (nullptr != apExchangeContext) { - apEc->Abort(); - apEc = NULL; + apExchangeContext->Abort(); + apExchangeContext = nullptr; } } -void InteractionModelEngine::OnMessageReceived(Messaging::ExchangeContext * apEc, const PacketHeader & aPacketHeader, +void InteractionModelEngine::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload) { if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::InvokeCommandRequest)) { - OnInvokeCommandRequest(apEc, aPacketHeader, aPayloadHeader, std::move(aPayload)); + + OnInvokeCommandRequest(apExchangeContext, aPacketHeader, aPayloadHeader, std::move(aPayload)); + } + else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::ReadRequest)) + { + OnReadRequest(apExchangeContext, aPacketHeader, aPayloadHeader, std::move(aPayload)); } else { - OnUnknownMsgType(apEc, aPacketHeader, aPayloadHeader, std::move(aPayload)); + OnUnknownMsgType(apExchangeContext, aPacketHeader, aPayloadHeader, std::move(aPayload)); } } @@ -206,5 +239,9 @@ DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aComman "Default DispatchSingleClusterCommand is called, this should be replaced by actual dispatched for cluster commands"); } +uint16_t InteractionModelEngine::GetReadClientArrayIndex(const ReadClient * const apClient) const +{ + return static_cast(apClient - mReadClients); +} } // namespace app } // namespace chip diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index 6112320403d498..2bd08fd417b86f 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -25,11 +25,8 @@ #pragma once -#ifndef _CHIP_INTERACTION_MODEL_ENGINE_H -#define _CHIP_INTERACTION_MODEL_ENGINE_H - +#include #include -#include #include #include #include @@ -43,17 +40,22 @@ #include #include #include +#include +#include +#include -#define CHIP_MAX_NUM_COMMAND_HANDLER_OBJECTS 1 -#define CHIP_MAX_NUM_COMMAND_SENDER_OBJECTS 1 +#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 namespace chip { namespace app { -constexpr size_t kMaxSecureSduLength = 1024; -constexpr uint32_t kImMesssageTimeout = 20; - -typedef void (*CommandCbFunct)(chip::TLV::TLVReader & aReader, Command * apCommandObj); +constexpr size_t kMaxSecureSduLength = 1024; +constexpr uint32_t kImMesssageTimeout = 3; +constexpr uint32_t kMinEventBoundarySize = 20; /** * @class InteractionModelEngine @@ -65,104 +67,66 @@ typedef void (*CommandCbFunct)(chip::TLV::TLVReader & aReader, Command * apComma class InteractionModelEngine : public Messaging::ExchangeDelegate { public: - enum EventID - { - kEvent_OnIncomingInvokeCommandRequest = - 0, ///< Called when an incoming invoke command request has arrived before applying commands.. - }; - /** - * Incoming parameters sent with events generated directly from this component + * @brief Set the delegate to associated state object for InteractionModelEngine specific call backs * + * @param[in] apDelegate InteractionModelDelegate set by application. */ - union InEventParam - { - void Clear(void) { memset(this, 0, sizeof(*this)); } - struct - { - const PacketHeader * mpPacketHeader; ///< A pointer to the message information for the request - } mIncomingInvokeCommandRequest; - }; + void SetDelegate(InteractionModelDelegate * apDelegate) { mpDelegate = apDelegate; }; /** - * Outgoing parameters sent with events generated directly from this component + * @brief Retrieve the singleton Interaction Model Engine. Note this function should be implemented by the + * adoption layer. * - */ - union OutEventParam - { - void Clear(void) { memset(this, 0, sizeof(*this)); } - - struct - { - bool mShouldContinueProcessing; ///< Set to true if update is allowed. - } mIncomingInvokeCommandRequest; - }; - - /** - * @brief Set the event back function and pointer to associated state object for SubscriptionEngine specific call backs + * @return A pointer to the shared InteractionModel Engine * - * @param[in] apAppState A pointer to application layer supplied state object - * @param[in] aEvent A function pointer for event call back - * @param[in] aInParam A const reference to the input parameter for this event - * @param[out] aOutParam A reference to the output parameter for this event */ - typedef void (*EventCallback)(void * apAppState, EventID aEvent, const InEventParam & aInParam, OutEventParam & aOutParam); + static InteractionModelEngine * GetInstance(void); - /** - * @brief Set the event back function and pointer to associated state object for SubscriptionEngine specific call backs - * - * @param[in] apAppState A pointer to application layer supplied state object - * @param[in] aEventCallback A function pointer for event call back - */ - void SetEventCallback(void * apAppState, EventCallback aEventCallback); + InteractionModelEngine(void); /** - * @brief This is the default event handler to be called by application layer for any ignored or unrecognized event + * Initialize the InteractionModel Engine. * - * @param[in] aEvent A function pointer for event call back - * @param[in] aInParam A const reference to the input parameter for this event - * @param[out] aOutParam A reference to the output parameter for this event - */ - static void DefaultEventHandler(EventID aEvent, const InEventParam & aInParam, OutEventParam & aOutParam); - - /** - * @brief Retrieve the singleton DataManagement Engine. Note this function should be implemented by the - * adoption layer. + * @param[in] apExchangeMgr A pointer to the ExchangeManager object. + * @param[in] apDelegate InteractionModelDelegate set by application. * - * @return A pointer to the shared InteractionModel Engine + * @retval #CHIP_ERROR_INCORRECT_STATE If the state is not equal to + * kState_NotInitialized. + * @retval #CHIP_NO_ERROR On success. * */ - static InteractionModelEngine * GetInstance(void); - - InteractionModelEngine(void); - - CHIP_ERROR Init(Messaging::ExchangeManager * apExchangeMgr); + CHIP_ERROR Init(Messaging::ExchangeManager * apExchangeMgr, InteractionModelDelegate * apDelegate); void Shutdown(); - CHIP_ERROR DeregisterClusterCommandHandler(chip::ClusterId aClusterId, chip::CommandId aCommandId, - Command::CommandRoleId aCommandRoleId); - CHIP_ERROR RegisterClusterCommandHandler(chip::ClusterId aClusterId, chip::CommandId aCommandId, - Command::CommandRoleId aCommandRoleId, CommandCbFunct aDispatcher); - Messaging::ExchangeManager * GetExchangeManager(void) const { return mpExchangeMgr; }; - CHIP_ERROR NewCommandSender(CommandSender ** const apComandSender); + CHIP_ERROR NewCommandSender(CommandSender ** const apCommandSender); + + CHIP_ERROR NewReadClient(ReadClient ** const apReadClient, InteractionModelDelegate * apDelegate); private: - void OnUnknownMsgType(Messaging::ExchangeContext * apEc, const PacketHeader & aPacketHeader, + friend class ReadClient; + void OnUnknownMsgType(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload); - void OnInvokeCommandRequest(Messaging::ExchangeContext * apEc, const PacketHeader & aPacketHeader, + void OnInvokeCommandRequest(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload); - void OnMessageReceived(Messaging::ExchangeContext * apEc, const PacketHeader & aPacketHeader, + void OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload); void OnResponseTimeout(Messaging::ExchangeContext * ec); + void OnReadRequest(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, + const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload); + + uint16_t GetReadClientArrayIndex(const ReadClient * const apClient) const; + Messaging::ExchangeManager * mpExchangeMgr = nullptr; - void * mpAppState = nullptr; - EventCallback mEventCallback; - CommandHandler mCommandHandlerObjs[CHIP_MAX_NUM_COMMAND_HANDLER_OBJECTS]; - CommandSender mCommandSenderObjs[CHIP_MAX_NUM_COMMAND_SENDER_OBJECTS]; + InteractionModelDelegate * mpDelegate = nullptr; + CommandHandler mCommandHandlerObjs[CHIP_MAX_NUM_COMMAND_HANDLER]; + CommandSender mCommandSenderObjs[CHIP_MAX_NUM_COMMAND_SENDER]; + ReadClient mReadClients[CHIP_MAX_NUM_READ_CLIENT]; + ReadHandler mReadHandlers[CHIP_MAX_NUM_READ_HANDLER]; }; void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aCommandId, chip::EndpointId aEndPointId, @@ -170,5 +134,3 @@ void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aC } // namespace app } // namespace chip - -#endif //_CHIP_INTERACTION_MODEL_ENGINE_H diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp new file mode 100644 index 00000000000000..dd1634a18b1429 --- /dev/null +++ b/src/app/ReadClient.cpp @@ -0,0 +1,252 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines read client for a CHIP Interaction Data model + * + */ + +#include +#include + +namespace chip { +namespace app { + +CHIP_ERROR ReadClient::Init(Messaging::ExchangeManager * apExchangeMgr, InteractionModelDelegate * apDelegate) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + // Error if already initialized. + VerifyOrExit(mpExchangeMgr == nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mpExchangeCtx == nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + mpExchangeMgr = apExchangeMgr; + mpExchangeCtx = nullptr; + mpDelegate = apDelegate; + mState = ClientState::Initialized; + +exit: + ChipLogFunctError(err); + return err; +} + +void ReadClient::Shutdown() +{ + ClearExistingExchangeContext(); + mpExchangeMgr = nullptr; + mpDelegate = nullptr; + MoveToState(ClientState::Uninitialized); +} + +void ReadClient::Reset() +{ + ClearExistingExchangeContext(); + MoveToState(ClientState::Initialized); +} + +const char * ReadClient::GetStateStr() const +{ +#if CHIP_DETAIL_LOGGING + switch (mState) + { + case ClientState::Uninitialized: + return "UNINIT"; + case ClientState::Initialized: + return "INIT"; + case ClientState::ReadRequestSending: + return "READREQUESTSENDING"; + } +#endif // CHIP_DETAIL_LOGGING + return "N/A"; +} + +void ReadClient::MoveToState(const ClientState aTargetState) +{ + mState = aTargetState; + ChipLogDetail(DataManagement, "Client[%u] moving to [%5.5s]", + InteractionModelEngine::GetInstance()->GetReadClientArrayIndex(this), GetStateStr()); +} + +void ReadClient::ClearState(void) +{ + MoveToState(ClientState::Uninitialized); +} + +CHIP_ERROR ReadClient::SendReadRequest(NodeId aNodeId, Transport::AdminId aAdminId) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + System::PacketBufferHandle msgBuf = nullptr; + chip::app::InteractionModelDelegate::InParam inParam; + chip::app::InteractionModelDelegate::OutParam outParam; + + ChipLogDetail(DataManagement, "Client[%u] [%5.5s]", InteractionModelEngine::GetInstance()->GetReadClientArrayIndex(this), + GetStateStr()); + VerifyOrExit(ClientState::Initialized == mState, err = CHIP_ERROR_INCORRECT_STATE); + + if (nullptr != mpDelegate) + { + outParam.mReadRequestPrepareNeeded.eventPathParamsList = nullptr; + mpDelegate->HandleIMCallBack(chip::app::InteractionModelDelegate::CallbackId::kReadRequestPrepareNeeded, inParam, outParam); + } + + { + chip::System::PacketBufferTLVWriter writer; + ReadRequest::Builder request; + + msgBuf = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLength); + VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY); + + writer.Init(std::move(msgBuf)); + + err = request.Init(&writer); + SuccessOrExit(err); + + request.EndOfReadRequest(); + SuccessOrExit(request.GetError()); + + err = writer.Finalize(&msgBuf); + SuccessOrExit(err); + + VerifyOrExit(msgBuf->EnsureReservedSize(System::PacketBuffer::kDefaultHeaderReserve), err = CHIP_ERROR_BUFFER_TOO_SMALL); + } + + ClearExistingExchangeContext(); + + mpExchangeCtx = mpExchangeMgr->NewContext({ aNodeId, 0, aAdminId }, this); + VerifyOrExit(mpExchangeCtx != nullptr, err = CHIP_ERROR_NO_MEMORY); + mpExchangeCtx->SetResponseTimeout(kImMesssageTimeout); + + err = mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::ReadRequest, std::move(msgBuf), + Messaging::SendFlags(Messaging::SendMessageFlags::kExpectResponse)); + SuccessOrExit(err); + MoveToState(ClientState::ReadRequestSending); + +exit: + ChipLogFunctError(err); + return err; +} + +void ReadClient::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, + const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + VerifyOrDie(apExchangeContext == mpExchangeCtx); + + ClearExistingExchangeContext(); + + err = ProcessReportData(std::move(aPayload)); + SuccessOrExit(err); + +exit: + Reset(); +} + +CHIP_ERROR ReadClient::ClearExistingExchangeContext() +{ + if (mpExchangeCtx != nullptr) + { + mpExchangeCtx->Abort(); + mpExchangeCtx = nullptr; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR ReadClient::ProcessReportData(System::PacketBufferHandle aPayload) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::app::InteractionModelDelegate::InParam inParam; + chip::app::InteractionModelDelegate::OutParam outParam; + ReportData::Parser report; + + bool isEventListPresent = false; + bool suppressResponse = false; + bool moreChunkedMessages = false; + + chip::System::PacketBufferTLVReader reader; + + reader.Init(std::move(aPayload)); + reader.Next(); + + err = report.Init(reader); + SuccessOrExit(err); + + err = report.CheckSchemaValidity(); + SuccessOrExit(err); + + err = report.GetSuppressResponse(&suppressResponse); + if (CHIP_END_OF_TLV == err) + err = CHIP_NO_ERROR; + SuccessOrExit(err); + + err = report.GetMoreChunkedMessages(&moreChunkedMessages); + if (CHIP_END_OF_TLV == err) + err = CHIP_NO_ERROR; + SuccessOrExit(err); + + { + EventList::Parser eventList; + + err = report.GetEventDataList(&eventList); + if (CHIP_NO_ERROR == err) + { + isEventListPresent = true; + } + else if (CHIP_END_OF_TLV == err) + { + isEventListPresent = false; + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); + + if (isEventListPresent && nullptr != mpDelegate && !moreChunkedMessages) + { + // re-initialize the reader (reuse to save stack depth). + inParam.Clear(); + outParam.Clear(); + eventList.GetReader(&reader); + inParam.mEventStreamReceived.reader = &reader; + mpDelegate->HandleIMCallBack(chip::app::InteractionModelDelegate::CallbackId::kEventStreamReceived, inParam, outParam); + } + } + + if (!suppressResponse) + { + // TODO: Add status report support and correspond handler in ReadHandler, particular for situation when there + // are multiple reports + } + if (nullptr != mpDelegate && !moreChunkedMessages) + { + inParam.Clear(); + outParam.Clear(); + mpDelegate->HandleIMCallBack(chip::app::InteractionModelDelegate::CallbackId::kReportProcessed, inParam, outParam); + } + +exit: + ChipLogFunctError(err); + return err; +} + +void ReadClient::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) +{ + ChipLogProgress(DataManagement, "Time out! failed to receive report data from Exchange: %d", + apExchangeContext->GetExchangeId()); + Reset(); +} +}; // namespace app +}; // namespace chip diff --git a/src/app/ReadClient.h b/src/app/ReadClient.h new file mode 100644 index 00000000000000..4080844806d3cd --- /dev/null +++ b/src/app/ReadClient.h @@ -0,0 +1,134 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines read client for a CHIP Interaction Data model + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +/** + * @class ReadClient + * + * @brief The read client is responsible for generating one read request with particular path on attributes and/or events, + * and expect to receive report data if any. + * + */ +class ReadClient : public Messaging::ExchangeDelegate +{ +public: + /** + * @brief Set delegate and pointer to associated state object for ReadClient specific call backs + * + * @param[in] apDelegate A fInteractionModelDelegate for event call back + */ + void SetDelegate(InteractionModelDelegate * apDelegate) { mpDelegate = apDelegate; }; + + void OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, + const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload) override; + void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override; + + /** + * Initialize the client object. Within the lifetime + * of this instance, this method is invoked once after object + * construction until a call to Shutdown is made to terminate the + * instance. + * + * @param[in] apExchangeMgr A pointer to the ExchangeManager object. + * @param[in] apDelegate InteractionModelDelegate set by application. + * + * @retval #CHIP_ERROR_INCORRECT_STATE incorrect state if it is already initialized + * @retval #CHIP_NO_ERROR On success. + * + */ + CHIP_ERROR Init(Messaging::ExchangeManager * apExchangeMgr, InteractionModelDelegate * apDelegate); + + /** + * Shutdown the Client. This terminates this instance + * of the object and releases all held resources. + * + */ + void Shutdown(); + + /** + * Reset the ReadClient to initialized state + * + */ + void Reset(); + + /** + * Send Read Request + * + * @param[in] aNodeId Node Id + * @param[in] aAdminId Admin ID + * + * @retval #others fail to send read request + * @retval #CHIP_NO_ERROR On success. + */ + CHIP_ERROR SendReadRequest(NodeId aNodeId, Transport::AdminId aAdminId); + + virtual ~ReadClient() = default; + + /** + * Check if current read client is being used + * + */ + bool IsFree() const { return (nullptr == mpExchangeCtx); }; + +private: + friend class InteractionModelEngine; + friend class TestInteractionModel; + + enum class ClientState + { + Uninitialized = 0, //< The client has not been initialized + Initialized, //< The client has been initialized and is ready + ReadRequestSending, //< The client has sent out the read request message + }; + + void MoveToState(const ClientState aTargetState); + CHIP_ERROR ProcessReportData(System::PacketBufferHandle aPayload); + CHIP_ERROR ClearExistingExchangeContext(); + void ClearState(); + const char * GetStateStr() const; + + Messaging::ExchangeManager * mpExchangeMgr = nullptr; + Messaging::ExchangeContext * mpExchangeCtx = nullptr; + InteractionModelDelegate * mpDelegate = nullptr; + ClientState mState = ClientState::Uninitialized; +}; + +}; // namespace app +}; // namespace chip diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp new file mode 100644 index 00000000000000..a580ff8a0abd76 --- /dev/null +++ b/src/app/ReadHandler.cpp @@ -0,0 +1,194 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines read handler for a CHIP Interaction Data model + * + */ + +#include +#include +#include + +namespace chip { +namespace app { +CHIP_ERROR ReadHandler::Init(Messaging::ExchangeManager * apExchangeMgr, InteractionModelDelegate * apDelegate) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + // Error if already initialized. + VerifyOrExit(mpExchangeMgr == nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + mpExchangeMgr = apExchangeMgr; + mpExchangeCtx = nullptr; + mpDelegate = apDelegate; + mSuppressResponse = true; + mGetToAllEvents = true; + mState = HandlerState::Initialized; + +exit: + ChipLogFunctError(err); + return err; +} + +void ReadHandler::Reset() +{ + ClearExistingExchangeContext(); + MoveToState(HandlerState::Initialized); +} + +void ReadHandler::Shutdown() +{ + ClearExistingExchangeContext(); + MoveToState(HandlerState::Uninitialized); + mpExchangeMgr = nullptr; + mpDelegate = nullptr; +} + +CHIP_ERROR ReadHandler::ClearExistingExchangeContext() +{ + if (mpExchangeCtx != nullptr) + { + mpExchangeCtx->Abort(); + mpExchangeCtx = nullptr; + } + + return CHIP_NO_ERROR; +} + +void ReadHandler::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, + const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + System::PacketBufferHandle response; + + mpExchangeCtx = apExchangeContext; + + err = ProcessReadRequest(std::move(aPayload)); + SuccessOrExit(err); + +exit: + ChipLogFunctError(err); + return; +} + +CHIP_ERROR ReadHandler::SendReportData(System::PacketBufferHandle && aPayload) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + VerifyOrExit(mpExchangeCtx != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + err = mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::ReportData, std::move(aPayload), + Messaging::SendFlags(Messaging::SendMessageFlags::kNone)); + SuccessOrExit(err); + MoveToState(HandlerState::ReportDataSending); + +exit: + Shutdown(); + ChipLogFunctError(err); + return err; +} + +CHIP_ERROR ReadHandler::ProcessReadRequest(System::PacketBufferHandle && aPayload) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::System::PacketBufferTLVReader reader; + + ReadRequest::Parser readRequestParser; + EventPathList::Parser eventPathListParser; + chip::TLV::TLVReader eventPathListReader; + + reader.Init(std::move(aPayload)); + + err = reader.Next(); + SuccessOrExit(err); + + err = readRequestParser.Init(reader); + SuccessOrExit(err); + + err = readRequestParser.CheckSchemaValidity(); + SuccessOrExit(err); + + err = readRequestParser.GetEventPathList(&eventPathListParser); + if (err == CHIP_END_OF_TLV) + { + err = CHIP_NO_ERROR; + } + else + { + SuccessOrExit(err); + eventPathListParser.GetReader(&eventPathListReader); + + while (CHIP_NO_ERROR == (err = eventPathListReader.Next())) + { + VerifyOrExit(chip::TLV::AnonymousTag == eventPathListReader.GetTag(), err = CHIP_ERROR_INVALID_TLV_TAG); + + EventPath::Parser eventPath; + + err = eventPath.Init(eventPathListReader); + SuccessOrExit(err); + } + } + + // 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 + switch (mState) + { + case HandlerState::Uninitialized: + return "Uninitialized"; + + case HandlerState::Initialized: + return "Initialized"; + + case HandlerState::ReportDataSending: + return "ReportDataSending"; + } +#endif // CHIP_DETAIL_LOGGING + return "N/A"; +} + +void ReadHandler::MoveToState(const HandlerState aTargetState) +{ + mState = aTargetState; + ChipLogDetail(DataManagement, "IM RH moving to [%10.10s]", GetStateStr()); +} + +void ReadHandler::ClearState(void) +{ + MoveToState(HandlerState::Uninitialized); +} + +void ReadHandler::OnReportProcessingComplete() +{ + ChipLogDetail(DataManagement, "OnReportProcessingComplete"); + Shutdown(); +} + +} // namespace app +} // namespace chip diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h new file mode 100644 index 00000000000000..36ec91d29913c7 --- /dev/null +++ b/src/app/ReadHandler.h @@ -0,0 +1,125 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines read handler for a CHIP Interaction Data model + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +/** + * @class ReadHandler + * + * @brief The read handler is responsible for processing read request, asks reporting engine to pack report data and sending + * reports via this handler. + * + */ +class ReadHandler +{ +private: + friend class InteractionModelEngine; + friend class TestInteractionModel; + + enum class HandlerState + { + Uninitialized = 0, //< The handler has not been initialized + Initialized, //< The handler has been initialized and is ready + ReportDataSending, //< The handler is sending out reportData + }; + + /** + * Initialize the ReadHandler. Within the lifetime + * of this instance, this method is invoked once after object + * construction until a call to Shutdown is made to terminate the + * instance. + * + * @param[in] apExchangeMgr A pointer to the ExchangeManager object. + * @param[in] apDelegate InteractionModelDelegate set by application. + * + * @retval #CHIP_ERROR_INCORRECT_STATE If the state is not equal to + * kState_NotInitialized. + * @retval #CHIP_NO_ERROR On success. + * + */ + CHIP_ERROR Init(Messaging::ExchangeManager * apExchangeMgr, InteractionModelDelegate * apDelegate); + + /** + * Shutdown the ReadHandler. This terminates this instance + * of the object and releases all held resources. + * + */ + void Shutdown(); + + /** + * Reset the ReadHandler to initialized state + * + */ + void Reset(); + + virtual ~ReadHandler() = default; + + bool IsFree() const { return (nullptr == mpExchangeCtx); }; + + void OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader, + const PayloadHeader & aPayloadHeader, System::PacketBufferHandle aPayload); + + CHIP_ERROR SendReportData(System::PacketBufferHandle && aPayload); + + CHIP_ERROR ProcessReadRequest(System::PacketBufferHandle && aPayload); + + void OnReportProcessingComplete(); + + bool IsReportable(void) { return (mState == HandlerState::Initialized); } + bool IsReporting(void) { return (mState == HandlerState::ReportDataSending); } + + void MoveToState(const HandlerState aTargetState); + void ClearState(); + const char * GetStateStr() const; + CHIP_ERROR ClearExistingExchangeContext(); + + Messaging::ExchangeManager * mpExchangeMgr = nullptr; + Messaging::ExchangeContext * mpExchangeCtx = nullptr; + InteractionModelDelegate * mpDelegate = nullptr; + + // Don't need the response for report data if true + bool mSuppressResponse; + + // Retrieve all events + bool mGetToAllEvents; + + // Current Handler state + HandlerState mState; +}; +} // namespace app +} // namespace chip diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 1bc2ccca58d0e7..2d79c73e60faf3 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -21,7 +21,10 @@ import("${chip_root}/build/chip/chip_test_suite.gni") chip_test_suite("tests") { output_name = "libAppTests" - test_sources = [ "TestMessageDef.cpp" ] + test_sources = [ + "TestInteractionModel.cpp", + "TestMessageDef.cpp", + ] cflags = [ "-Wconversion" ] diff --git a/src/app/tests/TestInteractionModel.cpp b/src/app/tests/TestInteractionModel.cpp new file mode 100644 index 00000000000000..2fa780f7b7771c --- /dev/null +++ b/src/app/tests/TestInteractionModel.cpp @@ -0,0 +1,193 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file implements a test for CHIP Interaction Model Message Def + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +chip::SecureSessionMgr gSessionManager; +chip::Messaging::ExchangeManager gExchangeManager; +chip::TransportMgr gTransportManager; +const chip::Transport::AdminId gAdminId = 0; + +namespace chip { +namespace app { +class TestInteractionModel +{ +public: + static void TestReadClient(nlTestSuite * apSuite, void * apContext); + static void TestReadHandler(nlTestSuite * apSuite, void * apContext); + +private: + static void GenerateReportData(nlTestSuite * apSuite, void * apContext, System::PacketBufferHandle && aPayload); +}; + +void TestInteractionModel::GenerateReportData(nlTestSuite * apSuite, void * apContext, System::PacketBufferHandle && aPayload) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::System::PacketBufferTLVWriter writer; + writer.Init(std::move(aPayload)); + + ReportData::Builder reportDataBuilder; + + err = reportDataBuilder.Init(&writer); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + reportDataBuilder.SuppressResponse(true); + NL_TEST_ASSERT(apSuite, reportDataBuilder.GetError() == CHIP_NO_ERROR); + + reportDataBuilder.MoreChunkedMessages(false); + NL_TEST_ASSERT(apSuite, reportDataBuilder.GetError() == CHIP_NO_ERROR); + + reportDataBuilder.EndOfReportData(); + NL_TEST_ASSERT(apSuite, reportDataBuilder.GetError() == CHIP_NO_ERROR); + + err = writer.Finalize(&aPayload); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); +} + +void TestInteractionModel::TestReadClient(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + chip::app::ReadClient readClient; + + chip::System::PacketBufferHandle buf = chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSize); + err = readClient.Init(&gExchangeManager, nullptr); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + err = readClient.SendReadRequest(chip::kTestDeviceNodeId, gAdminId); + NL_TEST_ASSERT(apSuite, err == CHIP_ERROR_NOT_CONNECTED); + + GenerateReportData(apSuite, apContext, buf.Retain()); + + err = readClient.ProcessReportData(std::move(buf)); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + readClient.Reset(); + readClient.Shutdown(); +} + +void TestInteractionModel::TestReadHandler(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::app::ReadHandler readHandler; + chip::System::PacketBufferTLVWriter writer; + chip::System::PacketBufferHandle reportDatabuf = chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSize); + chip::System::PacketBufferHandle readRequestbuf = chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSize); + ReadRequest::Builder readRequestBuilder; + readHandler.Init(&gExchangeManager, nullptr); + + GenerateReportData(apSuite, apContext, reportDatabuf.Retain()); + err = readHandler.SendReportData(std::move(reportDatabuf)); + NL_TEST_ASSERT(apSuite, err == CHIP_ERROR_INCORRECT_STATE); + + writer.Init(std::move(readRequestbuf)); + err = readRequestBuilder.Init(&writer); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + readRequestBuilder.EventNumber(1); + NL_TEST_ASSERT(apSuite, readRequestBuilder.GetError() == CHIP_NO_ERROR); + readRequestBuilder.EndOfReadRequest(); + NL_TEST_ASSERT(apSuite, readRequestBuilder.GetError() == CHIP_NO_ERROR); + err = writer.Finalize(&readRequestbuf); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + err = readHandler.ProcessReadRequest(std::move(readRequestbuf)); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + readHandler.Reset(); + readHandler.Shutdown(); +} + +} // 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); + + err = gSessionManager.Init(chip::kTestDeviceNodeId, &chip::DeviceLayer::SystemLayer, nullptr, &admins); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + err = gExchangeManager.Init(chip::kTestDeviceNodeId, &gTransportManager, &gSessionManager); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); +} + +/** + * Test Suite. It lists all the test functions. + */ + +// clang-format off +const nlTest sTests[] = + { + NL_TEST_DEF("CheckReadClient", chip::app::TestInteractionModel::TestReadClient), + NL_TEST_DEF("CheckReadHandler", chip::app::TestInteractionModel::TestReadHandler), + NL_TEST_SENTINEL() + }; +// clang-format on +} // namespace + +int TestEventLogging() +{ + // clang-format off + nlTestSuite theSuite = + { + "InteractionMessage", + &sTests[0], + nullptr, + nullptr + }; + // clang-format on + + InitializeChip(&theSuite); + + nlTestRunner(&theSuite, nullptr); + + return (nlTestRunnerStats(&theSuite)); +} + +CHIP_REGISTER_TEST_SUITE(TestEventLogging) diff --git a/src/app/tests/integration/chip_im_initiator.cpp b/src/app/tests/integration/chip_im_initiator.cpp index a536fa9e263ca9..7d28cbfb90308c 100644 --- a/src/app/tests/integration/chip_im_initiator.cpp +++ b/src/app/tests/integration/chip_im_initiator.cpp @@ -40,18 +40,21 @@ #define IM_CLIENT_PORT (CHIP_PORT + 1) namespace { +// Max value for the number of message request sent. +constexpr size_t kMaxCommandMessageCount = 3; +constexpr size_t kMaxReadMessageCount = 0; -// Max value for the number of command request sent. -constexpr size_t kMaxCommandCount = 3; - -// The CHIP Command interval time in milliseconds. -constexpr int32_t gCommandInterval = 1000; +// The CHIP Message interval time in milliseconds. +constexpr int32_t gMessageInterval = 1000; constexpr chip::Transport::AdminId gAdminId = 0; // The CommandSender object. chip::app::CommandSender * gpCommandSender = nullptr; +// The ReadClient object. +chip::app::ReadClient * gpReadClient = nullptr; + chip::TransportMgr gTransportManager; chip::SecureSessionMgr gSessionManager; @@ -59,30 +62,40 @@ chip::SecureSessionMgr gSessionManager; chip::Inet::IPAddress gDestAddr; // The last time a CHIP Command was attempted to be sent. -uint64_t gLastCommandTime = 0; +uint64_t gLastMessageTime = 0; // True, if the CommandSender is waiting for an CommandResponse // after sending an CommandRequest, false otherwise. bool gWaitingForCommandResp = false; +// True, if the ReadClient is waiting for an Report Data +// after sending an ReadRequest, false otherwise. +bool gWaitingForReadResp = false; + // Count of the number of CommandRequests sent. uint64_t gCommandCount = 0; // Count of the number of CommandResponses received. uint64_t gCommandRespCount = 0; -bool CommandIntervalExpired(void) +// Count of the number of CommandRequests sent. +uint64_t gReadCount = 0; + +// Count of the number of CommandResponses received. +uint64_t gReadRespCount = 0; + +bool MessageIntervalExpired(void) { uint64_t now = chip::System::Timer::GetCurrentEpoch(); - return (now >= gLastCommandTime + gCommandInterval); + return (now >= gLastMessageTime + gMessageInterval); } CHIP_ERROR SendCommandRequest(void) { CHIP_ERROR err = CHIP_NO_ERROR; - gLastCommandTime = chip::System::Timer::GetCurrentEpoch(); + gLastMessageTime = chip::System::Timer::GetCurrentEpoch(); printf("\nSend invoke command request message to Node: %" PRIu64 "\n", chip::kTestDeviceNodeId); @@ -135,6 +148,30 @@ CHIP_ERROR SendCommandRequest(void) return err; } +CHIP_ERROR SendReadRequest(void) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + gLastMessageTime = chip::System::Timer::GetCurrentEpoch(); + + printf("\nSend read request message to Node: %" PRIu64 "\n", chip::kTestDeviceNodeId); + + err = gpReadClient->SendReadRequest(chip::kTestDeviceNodeId, gAdminId); + SuccessOrExit(err); + + if (err == CHIP_NO_ERROR) + { + gWaitingForReadResp = true; + gReadCount++; + } + else + { + printf("Send read request failed, err: %s\n", chip::ErrorStr(err)); + } +exit: + return err; +} + CHIP_ERROR EstablishSecureSession() { CHIP_ERROR err = CHIP_NO_ERROR; @@ -152,7 +189,7 @@ CHIP_ERROR EstablishSecureSession() if (err != CHIP_NO_ERROR) { printf("Establish secure session failed, err: %s\n", chip::ErrorStr(err)); - gLastCommandTime = chip::System::Timer::GetCurrentEpoch(); + gLastMessageTime = chip::System::Timer::GetCurrentEpoch(); } else { @@ -162,6 +199,45 @@ CHIP_ERROR EstablishSecureSession() return err; } +void HandleReadComplete() +{ + uint32_t respTime = chip::System::Timer::GetCurrentEpoch(); + uint32_t transitTime = respTime - gLastMessageTime; + + gWaitingForReadResp = false; + gReadRespCount++; + + printf("Read Response: %" PRIu64 "/%" PRIu64 "(%.2f%%) time=%.3fms\n", gReadRespCount, gReadCount, + static_cast(gReadRespCount) * 100 / gReadCount, static_cast(transitTime) / 1000); +} + +class MockInteractionModelApp : public chip::app::InteractionModelDelegate +{ +public: + void HandleIMCallBack(chip::app::InteractionModelDelegate::CallbackId aCallbackId, + const chip::app::InteractionModelDelegate::InParam & aInParam, + chip::app::InteractionModelDelegate::OutParam & aOutParam) + { + switch (aCallbackId) + { + case chip::app::InteractionModelDelegate::CallbackId::kEventStreamReceived: { + ChipLogProgress(DataManagement, "Received Event stream"); + + break; + } + case chip::app::InteractionModelDelegate::CallbackId::kReportProcessed: { + HandleReadComplete(); + + break; + } + default: + chip::app::InteractionModelDelegate::DefaultCallbackIdHandler(aCallbackId, aInParam, aOutParam); + break; + } + return; + } +}; + } // namespace namespace chip { @@ -176,7 +252,7 @@ void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aC } uint32_t respTime = chip::System::Timer::GetCurrentEpoch(); - uint32_t transitTime = respTime - gLastCommandTime; + uint32_t transitTime = respTime - gLastMessageTime; if (aReader.GetLength() != 0) { @@ -194,6 +270,7 @@ void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aC int main(int argc, char * argv[]) { CHIP_ERROR err = CHIP_NO_ERROR; + MockInteractionModelApp mockDelegate; chip::Transport::AdminPairingTable admins; chip::Transport::AdminPairingInfo * adminInfo = admins.AssignAdminId(gAdminId, chip::kTestControllerNodeId); VerifyOrExit(adminInfo != nullptr, err = CHIP_ERROR_NO_MEMORY); @@ -223,7 +300,7 @@ int main(int argc, char * argv[]) err = gExchangeManager.Init(chip::kTestControllerNodeId, &gTransportManager, &gSessionManager); SuccessOrExit(err); - err = chip::app::InteractionModelEngine::GetInstance()->Init(&gExchangeManager); + err = chip::app::InteractionModelEngine::GetInstance()->Init(&gExchangeManager, &mockDelegate); SuccessOrExit(err); // Start the CHIP connection to the CHIP im responder. @@ -233,18 +310,21 @@ int main(int argc, char * argv[]) err = chip::app::InteractionModelEngine::GetInstance()->NewCommandSender(&gpCommandSender); SuccessOrExit(err); + err = chip::app::InteractionModelEngine::GetInstance()->NewReadClient(&gpReadClient, &mockDelegate); + SuccessOrExit(err); + // Connection has been established. Now send the CommandRequests. - for (unsigned int i = 0; i < kMaxCommandCount; i++) + for (unsigned int i = 0; i < kMaxCommandMessageCount; i++) { err = SendCommandRequest(); if (err != CHIP_NO_ERROR) { - printf("Send request failed: %s\n", chip::ErrorStr(err)); - break; + printf("Send command request failed: %s\n", chip::ErrorStr(err)); + goto exit; } - // Wait for response until the Command interval. - while (!CommandIntervalExpired()) + // Wait for response until the Message interval. + while (!MessageIntervalExpired()) { DriveIO(); } @@ -252,21 +332,51 @@ int main(int argc, char * argv[]) // Check if expected response was received. if (gWaitingForCommandResp) { - printf("No response received\n"); + printf("Invoke Command: No response received\n"); gWaitingForCommandResp = false; } } + // Connection has been established. Now send the ReadRequests. + for (unsigned int i = 0; i < kMaxReadMessageCount; i++) + { + err = SendReadRequest(); + if (err != CHIP_NO_ERROR) + { + printf("Send read request failed: %s\n", chip::ErrorStr(err)); + goto exit; + } + + // Wait for response until the Message interval. + while (!MessageIntervalExpired()) + { + DriveIO(); + } + + // Check if expected response was received. + if (gWaitingForReadResp) + { + printf("read request: No response received\n"); + gWaitingForReadResp = false; + } + } + gpCommandSender->Shutdown(); chip::app::InteractionModelEngine::GetInstance()->Shutdown(); ShutdownChip(); exit: - if ((err != CHIP_NO_ERROR) || (gCommandRespCount != kMaxCommandCount)) + if (err != CHIP_NO_ERROR || (gCommandRespCount != kMaxCommandMessageCount)) { printf("ChipCommandSender failed: %s\n", chip::ErrorStr(err)); exit(EXIT_FAILURE); } + if (err != CHIP_NO_ERROR || (gReadRespCount != kMaxReadMessageCount)) + { + printf("ChipReadClient failed: %s\n", chip::ErrorStr(err)); + exit(EXIT_FAILURE); + } + printf("Test success \n"); return EXIT_SUCCESS; } diff --git a/src/app/tests/integration/chip_im_responder.cpp b/src/app/tests/integration/chip_im_responder.cpp index f4f3d3dfcbb57f..352d13090492fd 100644 --- a/src/app/tests/integration/chip_im_responder.cpp +++ b/src/app/tests/integration/chip_im_responder.cpp @@ -20,18 +20,19 @@ * This file implements a chip-im-responder, for the * CHIP Interaction Data Model Protocol. * - * Currently it provides simple command handler with sample cluster and command + * Currently it provides simple command and read handler with sample cluster * */ -#include "app/InteractionModelEngine.h" #include #include +#include #include #include +#include +#include +#include #include - -#include "InteractionModelEngine.h" #include #include #include @@ -40,7 +41,6 @@ namespace chip { namespace app { - void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aCommandId, chip::EndpointId aEndPointId, chip::TLV::TLVReader & aReader, Command * apCommandObj) { @@ -97,17 +97,32 @@ void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aC } // namespace chip namespace { - -// The CommandHandler object chip::TransportMgr gTransportManager; chip::SecureSessionMgr gSessionManager; chip::SecurePairingUsingTestSecret gTestPairing; +class MockInteractionModelApp : public chip::app::InteractionModelDelegate +{ +public: + void HandleIMCallBack(chip::app::InteractionModelDelegate::CallbackId aCallbackId, + const chip::app::InteractionModelDelegate::InParam & aInParam, + chip::app::InteractionModelDelegate::OutParam & aOutParam) + { + switch (aCallbackId) + { + default: + chip::app::InteractionModelDelegate::DefaultCallbackIdHandler(aCallbackId, aInParam, aOutParam); + break; + } + } +}; + } // namespace int main(int argc, char * argv[]) { CHIP_ERROR err = CHIP_NO_ERROR; + MockInteractionModelApp mockDelegate; chip::Optional peer(chip::Transport::Type::kUndefined); const chip::Transport::AdminId gAdminId = 0; chip::Transport::AdminPairingTable admins; @@ -127,7 +142,7 @@ int main(int argc, char * argv[]) err = gExchangeManager.Init(chip::kTestDeviceNodeId, &gTransportManager, &gSessionManager); SuccessOrExit(err); - err = chip::app::InteractionModelEngine::GetInstance()->Init(&gExchangeManager); + err = chip::app::InteractionModelEngine::GetInstance()->Init(&gExchangeManager, &mockDelegate); SuccessOrExit(err); err = gSessionManager.NewPairing(peer, chip::kTestControllerNodeId, &gTestPairing, @@ -142,7 +157,7 @@ int main(int argc, char * argv[]) if (err != CHIP_NO_ERROR) { - printf("CommandHandler failed, err:%s\n", chip::ErrorStr(err)); + printf("IM responder failed, err:%s\n", chip::ErrorStr(err)); exit(EXIT_FAILURE); }