diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 55bc5f305ddd99..666c452b09dc3a 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -207,7 +207,7 @@ static_library("interaction-model") { public_deps = [ ":app_config", - ":command-handler", + ":command-handler-impl", ":constants", ":paths", ":subscription-info-provider", @@ -333,16 +333,32 @@ source_set("status-response") { ] } -source_set("command-handler") { +source_set("command-handler-interface") { sources = [ "CommandHandler.cpp", "CommandHandler.h", "CommandHandlerExchangeInterface.h", + ] + + public_deps = [ + ":paths", + "${chip_root}/src/access:types", + "${chip_root}/src/app/data-model", + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/support", + "${chip_root}/src/messaging", + "${chip_root}/src/protocols/interaction_model", + ] +} + +source_set("command-handler-impl") { + sources = [ "CommandHandlerImpl.cpp", "CommandHandlerImpl.h", ] public_deps = [ + ":command-handler-interface", ":paths", ":required-privileges", ":status-response", diff --git a/src/app/codegen-data-model/CodegenDataModel.cpp b/src/app/codegen-data-model/CodegenDataModel.cpp index 0cc9b42aaa64f7..851f7c341ab127 100644 --- a/src/app/codegen-data-model/CodegenDataModel.cpp +++ b/src/app/codegen-data-model/CodegenDataModel.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -232,10 +233,19 @@ bool CodegenDataModel::EmberCommandListIterator::Exists(const CommandId * list, } CHIP_ERROR CodegenDataModel::Invoke(const InteractionModel::InvokeRequest & request, TLV::TLVReader & input_arguments, - InteractionModel::InvokeReply & reply) + CommandHandler * handler) { - // TODO: this needs an implementation - return CHIP_ERROR_NOT_IMPLEMENTED; + // TODO: CommandHandlerInterface support is currently + // residing in InteractionModelEngine itself. We may want to separate this out + // into its own registry, similar to attributes, so that IM is decoupled from actual storage of things. + // + // Open issue at https://github.com/project-chip/connectedhomeip/issues/34258 + + // Ember dispatching automatically uses `handler` to set an appropriate result or status + // This never fails (as handler error is encoded as needed). + DispatchSingleClusterCommand(request.path, input_arguments, handler); + + return CHIP_NO_ERROR; } EndpointId CodegenDataModel::FirstEndpoint() diff --git a/src/app/codegen-data-model/CodegenDataModel.h b/src/app/codegen-data-model/CodegenDataModel.h index b65f38b9155e73..123dcd72382291 100644 --- a/src/app/codegen-data-model/CodegenDataModel.h +++ b/src/app/codegen-data-model/CodegenDataModel.h @@ -71,7 +71,7 @@ class CodegenDataModel : public chip::app::InteractionModel::DataModel CHIP_ERROR ReadAttribute(const InteractionModel::ReadAttributeRequest & request, AttributeValueEncoder & encoder) override; CHIP_ERROR WriteAttribute(const InteractionModel::WriteAttributeRequest & request, AttributeValueDecoder & decoder) override; CHIP_ERROR Invoke(const InteractionModel::InvokeRequest & request, chip::TLV::TLVReader & input_arguments, - InteractionModel::InvokeReply & reply) override; + CommandHandler * handler) override; /// attribute tree iteration EndpointId FirstEndpoint() override; diff --git a/src/app/codegen-data-model/tests/BUILD.gn b/src/app/codegen-data-model/tests/BUILD.gn index 3d265a96a66b29..3f88ac61c41766 100644 --- a/src/app/codegen-data-model/tests/BUILD.gn +++ b/src/app/codegen-data-model/tests/BUILD.gn @@ -24,6 +24,8 @@ source_set("ember_extra_files") { "${chip_root}/src/app/util/ember-io-storage.cpp", "AttributeReportIBEncodeDecode.cpp", "AttributeReportIBEncodeDecode.h", + "EmberInvokeOverride.cpp", + "EmberInvokeOverride.h", "EmberReadWriteOverride.cpp", "EmberReadWriteOverride.h", "InteractionModelTemporaryOverrides.cpp", diff --git a/src/app/codegen-data-model/tests/EmberInvokeOverride.cpp b/src/app/codegen-data-model/tests/EmberInvokeOverride.cpp new file mode 100644 index 00000000000000..552e0be3d966ff --- /dev/null +++ b/src/app/codegen-data-model/tests/EmberInvokeOverride.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 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. + */ +#include "EmberInvokeOverride.h" + +#include + +namespace { + +chip::app::ConcreteCommandPath gLastDispatchPath; +uint32_t gDispatchCount = 0; + +} // namespace + +namespace chip { +namespace Test { + +app::ConcreteCommandPath GetLastDispatchPath() +{ + return gLastDispatchPath; +} + +uint32_t DispatchCount() +{ + return gDispatchCount; +} + +} // namespace Test +} // namespace chip + +namespace chip { +namespace app { + +void DispatchSingleClusterCommand(const ConcreteCommandPath & aRequestCommandPath, chip::TLV::TLVReader & aReader, + CommandHandler * apCommandObj) +{ + gLastDispatchPath = aRequestCommandPath; + gDispatchCount++; +} + +} // namespace app +} // namespace chip diff --git a/src/app/codegen-data-model/tests/EmberInvokeOverride.h b/src/app/codegen-data-model/tests/EmberInvokeOverride.h new file mode 100644 index 00000000000000..d2f7b5c32fb1f8 --- /dev/null +++ b/src/app/codegen-data-model/tests/EmberInvokeOverride.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 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. + */ +#pragma once + +#include + +namespace chip { +namespace Test { + +/// what was the last path on which DispatchSingleClusterCommand was called +app::ConcreteCommandPath GetLastDispatchPath(); + +/// How many times was DispatchSingleClusterCommand called +uint32_t DispatchCount(); + +} // namespace Test +} // namespace chip diff --git a/src/app/codegen-data-model/tests/InteractionModelTemporaryOverrides.cpp b/src/app/codegen-data-model/tests/InteractionModelTemporaryOverrides.cpp index a595acc81f3b5c..e771827adf18f6 100644 --- a/src/app/codegen-data-model/tests/InteractionModelTemporaryOverrides.cpp +++ b/src/app/codegen-data-model/tests/InteractionModelTemporaryOverrides.cpp @@ -74,12 +74,6 @@ CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescr return CHIP_ERROR_NOT_IMPLEMENTED; } -void DispatchSingleClusterCommand(const ConcreteCommandPath & aRequestCommandPath, chip::TLV::TLVReader & aReader, - CommandHandler * apCommandObj) -{ - // TODO: total hardcoded noop -} - } // namespace app } // namespace chip diff --git a/src/app/codegen-data-model/tests/TestCodegenModelViaMocks.cpp b/src/app/codegen-data-model/tests/TestCodegenModelViaMocks.cpp index 2bf88a02bae6ef..e0fd4f6148f7c4 100644 --- a/src/app/codegen-data-model/tests/TestCodegenModelViaMocks.cpp +++ b/src/app/codegen-data-model/tests/TestCodegenModelViaMocks.cpp @@ -14,9 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "app/ConcreteCommandPath.h" #include #include +#include #include #include @@ -69,6 +71,9 @@ constexpr NodeId kTestNodeId = 0xFFFF'1234'ABCD'4321; constexpr AttributeId kAttributeIdReadOnly = 0x3001; constexpr AttributeId kAttributeIdTimedWrite = 0x3002; +constexpr CommandId kMockCommandId1 = 0x1234; +constexpr CommandId kMockCommandId2 = 0x1122; + constexpr EndpointId kEndpointIdThatIsMissing = kMockEndpointMin - 1; constexpr AttributeId kReadOnlyAttributeId = 0x5001; @@ -2430,6 +2435,47 @@ TEST(TestCodegenModelViaMocks, EmberWriteAttributeAccessInterfaceTest) TestEmberScalarNullWrite(); } +TEST(TestCodegenModelViaMocks, EmberInvokeTest) +{ + // Ember invoke is fully code-generated - there is a single function for Dispatch + // that will do a `switch` on the path elements and invoke a corresponding `emberAf*` + // callback. + // + // The only thing that can be validated is that this `DispatchSingleClusterCommand` + // is actually invoked. + + UseMockNodeConfig config(gTestNodeConfig); + chip::app::CodegenDataModel model; + + { + const ConcreteCommandPath kCommandPath(kMockEndpoint1, MockClusterId(1), kMockCommandId1); + const InvokeRequest kInvokeRequest{ .path = kCommandPath }; + chip::TLV::TLVReader tlvReader; + + const uint32_t kDispatchCountPre = chip::Test::DispatchCount(); + + // Using a handler set to nullptr as it is not used by the impl + ASSERT_EQ(model.Invoke(kInvokeRequest, tlvReader, /* handler = */ nullptr), CHIP_NO_ERROR); + + EXPECT_EQ(chip::Test::DispatchCount(), kDispatchCountPre + 1); // single dispatch + EXPECT_EQ(chip::Test::GetLastDispatchPath(), kCommandPath); // for the right path + } + + { + const ConcreteCommandPath kCommandPath(kMockEndpoint1, MockClusterId(1), kMockCommandId2); + const InvokeRequest kInvokeRequest{ .path = kCommandPath }; + chip::TLV::TLVReader tlvReader; + + const uint32_t kDispatchCountPre = chip::Test::DispatchCount(); + + // Using a handler set to nullpotr as it is not used by the impl + ASSERT_EQ(model.Invoke(kInvokeRequest, tlvReader, /* handler = */ nullptr), CHIP_NO_ERROR); + + EXPECT_EQ(chip::Test::DispatchCount(), kDispatchCountPre + 1); // single dispatch + EXPECT_EQ(chip::Test::GetLastDispatchPath(), kCommandPath); // for the right path + } +} + TEST(TestCodegenModelViaMocks, EmberWriteAttributeAccessInterfaceReturningError) { UseMockNodeConfig config(gTestNodeConfig); diff --git a/src/app/data-model-interface/BUILD.gn b/src/app/data-model-interface/BUILD.gn index 65cde75612e2ba..abe66a68f342bd 100644 --- a/src/app/data-model-interface/BUILD.gn +++ b/src/app/data-model-interface/BUILD.gn @@ -20,7 +20,6 @@ source_set("data-model-interface") { "DataModel.h", "DataModelChangeListener.h", "EventsGenerator.h", - "InvokeResponder.h", "MetadataTypes.cpp", "MetadataTypes.h", "OperationTypes.h", @@ -29,6 +28,7 @@ source_set("data-model-interface") { public_deps = [ "${chip_root}/src/access:types", "${chip_root}/src/app:attribute-access", + "${chip_root}/src/app:command-handler-interface", "${chip_root}/src/app:events", "${chip_root}/src/app:paths", "${chip_root}/src/app/MessageDef", diff --git a/src/app/data-model-interface/DataModel.h b/src/app/data-model-interface/DataModel.h index d673b79aac72a9..bcf24c1aa9e7b2 100644 --- a/src/app/data-model-interface/DataModel.h +++ b/src/app/data-model-interface/DataModel.h @@ -21,9 +21,9 @@ #include #include +#include #include -#include #include #include @@ -99,25 +99,9 @@ class DataModel : public DataModelMetadataTree /// - `NeedsTimedInteraction` for writes that are not timed however are required to be so virtual CHIP_ERROR WriteAttribute(const WriteAttributeRequest & request, AttributeValueDecoder & decoder) = 0; - /// `reply` is used to send back the reply. - /// - calling Reply() or ReplyAsync() will let the application control the reply - /// - returning a CHIP_NO_ERROR without reply/reply_async implies a Status::Success reply without data + /// `handler` is used to send back the reply. /// - returning a value other than CHIP_NO_ERROR implies an error reply (error and data are mutually exclusive) - /// - /// See InvokeReply/AutoCompleteInvokeResponder for details on how to send back replies and expected - /// error handling. If you need to know weather a response was successfully sent, use the underlying - /// `reply` object instead of returning an error code from Invoke. - /// - /// Return codes - /// CHIP_IM_GLOBAL_STATUS(code): - /// - error codes that are translatable to specific IM codes - /// - in particular, the following codes are interesting/expected - /// - `UnsupportedEndpoint` for invalid endpoint - /// - `UnsupportedCluster` for no such cluster on the endpoint - /// - `UnsupportedCommand` for no such command in the cluster - /// - `UnsupportedAccess` for permission errors (ACL or fabric scoped with invalid fabric) - /// - `NeedsTimedInteraction` if the invoke requires timed interaction support - virtual CHIP_ERROR Invoke(const InvokeRequest & request, chip::TLV::TLVReader & input_arguments, InvokeReply & reply) = 0; + virtual CHIP_ERROR Invoke(const InvokeRequest & request, chip::TLV::TLVReader & input_arguments, CommandHandler * handler) = 0; private: InteractionModelContext mContext = { nullptr }; diff --git a/src/app/data-model-interface/InvokeResponder.h b/src/app/data-model-interface/InvokeResponder.h deleted file mode 100644 index 9890c3bb6f6d7e..00000000000000 --- a/src/app/data-model-interface/InvokeResponder.h +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (c) 2024 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. - */ -#pragma once - -#include -#include -#include - -namespace chip { -namespace app { -namespace InteractionModel { - -/// Handles encoding of an invoke response for a specific invoke request. -/// -/// This class handles a single request (i.e. a CommandDataIB within the -/// matter protocol) and is responsible for constructing its corresponding -/// response (i.e. a InvokeResponseIB within the matter protocol) -/// -/// Invoke responses MUST contain exactly ONE of: -/// - response data (accessed via `ResponseEncoder`) -/// - A status, which may be success or failure, both of which may -/// contain a cluster-specific error code. -/// -/// To encode a response, `Complete` MUST be called. -/// -/// `Complete` requirements -/// - Complete with InteractionModel::Status::Success will respond with data -/// some response data was written. -/// - Any other case (including success with cluster specific codes) implies -/// no response data and a status will be encoded instead -/// - this includes the case when some response data was written already. -/// In that case, the response data will be rolled back and only the status -/// will be encoded. -/// -/// Creating a response MAY be retried at most once, if and only if `Complete` -/// returns CHIP_ERROR_BUFFER_TOO_SMALL. Retry attempts MUST not exceed 1: -/// - FlushPendingResponses MUST be called to make as much buffer space as possible -/// available for encoding -/// - The response encoding (including `ResponseEncoder` usage and calling Complete) -/// MUST be retried once more. If the final Complete returns an error, the result -/// of the invoke will be an error status. -/// -class InvokeResponder -{ -public: - virtual ~InvokeResponder() = default; - - // Copying not allowed since underlying requirement is that on deletion of this - // object, a reply will be sent. - InvokeResponder(const InvokeResponder &) = delete; - InvokeResponder & operator=(const InvokeResponder &) = delete; - - /// Flush any pending replies before encoding the current reply. - /// - /// MAY be called at most once. - /// - /// This function is intended to provided the ability to retry sending a reply - /// if a reply encoding fails due to insufficient buffer. - /// - /// Call this if `Complete(...)` returns CHIP_ERROR_BUFFER_TOO_SMALL and try - /// again. If reply data is needed, the complete ResponseEncoder + Complete - /// call chain MUST be re-run. - virtual CHIP_ERROR FlushPendingResponses() = 0; - - /// Reply with a data payload. - /// - /// MUST be called at most once per reply. - /// Can be called a 2nd time after a `FlushPendingResponses()` call - /// - /// - responseCommandId must correspond with the data encoded in the returned encoder - /// - Complete(CHIP_NO_ERROR) MUST be called to flush the reply - /// - /// If encoder returns CHIP_ERROR_BUFFER_TOO_SMALL, FlushPendingResponses should be - /// used to attempt to free up buffer space then encoding should be tried again. - virtual DataModel::WrappedStructEncoder & ResponseEncoder(CommandId responseCommandId) = 0; - - /// Signal completing of the reply. - /// - /// MUST be called exactly once to signal a response is to be recorded to be sent. - /// The error code (and the data encoded by ResponseEncoder) may be buffered for - /// sending among other batched responses. - /// - /// If this returns CHIP_ERROR_BUFFER_TOO_SMALL, this can be called a 2nd time after - /// a FlushPendingResponses. - /// - /// Argument behavior: - /// - Commands can only be replied with ONE of the following (spec 8.9.4.4): - /// - command data (i.e. ResponseEncoder contents) - /// - A status (including success/error/cluster-specific-success-or-error ) - /// - As a result there are two possible paths: - /// - IF a Status::Success is given (WITHOUT cluster specific status), then - /// the data in ResponseEncoder is sent as a reply. If no data was sent, - /// a invoke `Status::Success` with no cluster specific data is sent - /// - OTHERWISE any previously encoded data via ResponseEncoder is discarded - /// and the given reply (success with cluster status or failure) is sent - /// as a reply to the invoke. - /// - /// - /// Returns success/failure state. One error code MUST be handled in particular: - /// - /// - CHIP_ERROR_BUFFER_TOO_SMALL will return IF AND ONLY IF the responder was unable - /// to fully serialize the given reply/error data. - /// - /// If such an error is returned, the caller MUST retry by calling FlushPendingResponses - /// first and then re-encoding the reply content (use ResponseEncoder if applicable and - /// call Complete again) - /// - /// - Any other error (i.e. different from CHIP_NO_ERROR) mean that the invoke response - /// will contain an error and such an error is considered permanent. - /// - virtual CHIP_ERROR Complete(StatusIB error) = 0; -}; - -/// Enforces that once acquired, Complete will be called on the underlying writer -class AutoCompleteInvokeResponder -{ -public: - // non-copyable: once you have a handle, keep it - AutoCompleteInvokeResponder(const AutoCompleteInvokeResponder &) = delete; - AutoCompleteInvokeResponder & operator=(const AutoCompleteInvokeResponder &) = delete; - - AutoCompleteInvokeResponder(InvokeResponder * writer) : mWriter(writer) {} - ~AutoCompleteInvokeResponder() - { - if (mCompleteState != CompleteState::kComplete) - { - mWriter->Complete(StatusIB{ Protocols::InteractionModel::Status::Failure }); - } - } - - /// Direct access to reply encoding. - /// - /// Use this only in conjunction with the other Raw* calls - DataModel::WrappedStructEncoder & RawResponseEncoder(CommandId replyCommandId) - { - return mWriter->ResponseEncoder(replyCommandId); - } - - /// Direct access to flushing replies - /// - /// Use this only in conjunction with the other Raw* calls - CHIP_ERROR RawFlushPendingReplies() - { - // allow a flush if we never called it (this may not be reasonable, however - // we accept an early flush) or if flush is expected - VerifyOrReturnError((mCompleteState == CompleteState::kNeverCalled) || (mCompleteState == CompleteState::kFlushExpected), - CHIP_ERROR_INCORRECT_STATE); - mCompleteState = CompleteState::kFlushed; - return mWriter->FlushPendingResponses(); - } - - /// Call "Complete" without the automatic retries. - /// - /// Use this in conjunction with the other Raw* calls - CHIP_ERROR RawComplete(StatusIB status) - { - VerifyOrReturnError((mCompleteState == CompleteState::kNeverCalled) || (mCompleteState == CompleteState::kFlushed), - CHIP_ERROR_INCORRECT_STATE); - CHIP_ERROR err = mWriter->Complete(status); - if ((err == CHIP_ERROR_BUFFER_TOO_SMALL) && (mCompleteState == CompleteState::kNeverCalled)) - { - mCompleteState = CompleteState::kFlushExpected; - } - else - { - mCompleteState = CompleteState::kComplete; - } - return err; - } - - /// Complete the given command. - /// - /// Automatically handles retries for sending. - /// Cannot be called after Raw* methods are used. - /// - /// Any error returned by this are final and not retriable - /// as a retry for CHIP_ERROR_BUFFER_TOO_SMALL is already built in. - CHIP_ERROR Complete(StatusIB status) - { - VerifyOrReturnError(mCompleteState == CompleteState::kNeverCalled, CHIP_ERROR_INCORRECT_STATE); - // this is a final complete, including retry handling - mCompleteState = CompleteState::kComplete; - CHIP_ERROR err = mWriter->Complete(status); - - if (err != CHIP_ERROR_BUFFER_TOO_SMALL) - { - return err; - } - - // retry once. Failure to flush is permanent. - ReturnErrorOnFailure(mWriter->FlushPendingResponses()); - return mWriter->Complete(status); - } - - /// Sends the specified data structure as a response - /// - /// This version of the send has built-in RETRY and handles - /// Flush/Complete automatically. - /// Cannot be called after Raw* methods are used. - /// - /// Any error returned by this are final and not retriable - /// as a retry for CHIP_ERROR_BUFFER_TOO_SMALL is already built in. - template - CHIP_ERROR Send(const ReplyData & data) - { - VerifyOrReturnError(mCompleteState == CompleteState::kNeverCalled, CHIP_ERROR_INCORRECT_STATE); - // this is a final complete, including retry handling - mCompleteState = CompleteState::kComplete; - CHIP_ERROR err = data.Encode(ResponseEncoder(ReplyData::GetCommandId())); - if (err != CHIP_ERROR_BUFFER_TOO_SMALL) - { - LogErrorOnFailure(err); - err = mWriter->Complete(StatusIB(err)); - } - if (err != CHIP_ERROR_BUFFER_TOO_SMALL) - { - return err; - } - - // retry once. Failure to flush is permanent. - ReturnErrorOnFailure(mWriter->FlushPendingResponses()); - err = data.Encode(ResponseEncoder(ReplyData::GetCommandId())); - - // If encoding fails, we will end up sending an error back to the other side - // the caller - LogErrorOnFailure(err); - if (err == CHIP_NO_ERROR) - { - err = mWriter->Complete(StatusIB(err)); - } - else - { - // Error in "complete" is not something we can really forward anymore since - // we already got an error in Encode ... just log this. - LogErrorOnFailure(mWriter->Complete(StatusIB(err))); - } - - return err; - } - -private: - // Contract says that complete may only be called twice: - // - initial complete - // - again after a `Flush` - // The states here expect we are in: - // - // +----------------------------Flush---------| - // | v - // NEVER --Complete--> F_EXPECTED --Flush--> FLUSHED --Complete--> COMPLETE - // | ^ - // +-------------(success or permanent error)-----------| - enum class CompleteState - { - kNeverCalled, - kFlushExpected, - kFlushed, - kComplete, - }; - - InvokeResponder * mWriter; - CompleteState mCompleteState = CompleteState::kNeverCalled; -}; - -enum ReplyAsyncFlags -{ - // Some commands that are expensive to process (e.g. crypto). - // Implementations may choose to send an ack on the message right away to - // avoid MRP retransmits. - kSlowCommandHandling = 0x0001, -}; - -class InvokeReply -{ -public: - virtual ~InvokeReply() = default; - - // reply with no data - CHIP_ERROR Reply(StatusIB status) { return this->Reply().Complete(status); } - - // Enqueue the content of the reply at this point in time (rather than Async sending it). - // - // Implementations will often batch several replies into one packet for batch commands, - // so it will be implementation-specific on when the actual reply packet is - // sent. - virtual AutoCompleteInvokeResponder Reply() = 0; - - // Reply "later" to the command. This allows async processing. A reply will be forced - // when the returned InvokeReply is destroyed. - // - // NOTE: Each InvokeReply is associated with a separate `CommandDataIB` within batch - // commands. When replying asynchronously, each InvokeReply will set the response - // data for the given commandpath/ref only. - // - // IF empty pointer is returned, insufficient memory to reply async is available and - // this should be handled (e.g. by returning an error to the handler/replying with - // an errorcode synchronously). - virtual std::unique_ptr ReplyAsync(BitFlags flags) = 0; -}; - -} // namespace InteractionModel -} // namespace app -} // namespace chip