|  | /* | 
|  | * | 
|  | *    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. | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include <controller/TypedCommandCallback.h> | 
|  | #include <lib/core/Optional.h> | 
|  |  | 
|  | #include <functional> | 
|  |  | 
|  | namespace chip { | 
|  | namespace Controller { | 
|  |  | 
|  | namespace Internal { | 
|  | // Cancellation functions on InvokeCommandRequest() are for internal use only. | 
|  | typedef std::function<void()> InvokeCancelFn; | 
|  | } // namespace Internal | 
|  |  | 
|  | /* | 
|  | * A typed command invocation function that takes as input a cluster-object representation of a command request and | 
|  | * callbacks for success and failure and either returns a decoded cluster-object representation of the response through | 
|  | * the provided success callback or calls the provided failure callback. | 
|  | * | 
|  | * The RequestObjectT is generally expected to be a ClusterName::Commands::CommandName::Type struct, but any object | 
|  | * that can be encoded using the DataModel::Encode machinery and exposes the | 
|  | * GetClusterId() and GetCommandId() functions and a ResponseType type | 
|  | * is expected to work. | 
|  | * | 
|  | * The ResponseType is expected to be one of two things: | 
|  | * | 
|  | *    - If a data response is expected on success, a struct type decodable via DataModel::Decode which has GetClusterId() and | 
|  | * GetCommandId() methods.  A ClusterName::Commands::ResponseCommandName::DecodableType is typically used. | 
|  | *    - If a status response is expected on success, DataModel::NullObjectType. | 
|  | * | 
|  | */ | 
|  | template <typename RequestObjectT> | 
|  | CHIP_ERROR | 
|  | InvokeCommandRequest(Messaging::ExchangeManager * aExchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId, | 
|  | const RequestObjectT & requestCommandData, | 
|  | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb, | 
|  | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb, | 
|  | const Optional<uint16_t> & timedInvokeTimeoutMs, | 
|  | const Optional<System::Clock::Timeout> & responseTimeout = NullOptional, | 
|  | Internal::InvokeCancelFn * outCancelFn                   = nullptr) | 
|  | { | 
|  | // InvokeCommandRequest expects responses, so cannot happen over a group session. | 
|  | VerifyOrReturnError(!sessionHandle->IsGroupSession(), CHIP_ERROR_INVALID_ARGUMENT); | 
|  |  | 
|  | app::CommandPathParams commandPath = { endpointId, 0, RequestObjectT::GetClusterId(), RequestObjectT::GetCommandId(), | 
|  | (app::CommandPathFlags::kEndpointIdValid) }; | 
|  |  | 
|  | // | 
|  | // Let's create a handle version of the decoder to ensure we do correct clean-up of it if things go south at any point below | 
|  | // | 
|  | auto decoder = chip::Platform::MakeUnique<TypedCommandCallback<typename RequestObjectT::ResponseType>>(onSuccessCb, onErrorCb); | 
|  | VerifyOrReturnError(decoder != nullptr, CHIP_ERROR_NO_MEMORY); | 
|  |  | 
|  | // | 
|  | // Upon successful completion of SendCommandRequest below, we're expected to free up the respective allocated objects | 
|  | // in the OnDone callback. | 
|  | // | 
|  | auto onDone = [rawDecoderPtr = decoder.get()](app::CommandSender * commandSender) { | 
|  | chip::Platform::Delete(commandSender); | 
|  | chip::Platform::Delete(rawDecoderPtr); | 
|  | }; | 
|  |  | 
|  | decoder->SetOnDoneCallback(onDone); | 
|  |  | 
|  | auto commandSender = | 
|  | chip::Platform::MakeUnique<app::CommandSender>(decoder.get(), aExchangeMgr, timedInvokeTimeoutMs.HasValue()); | 
|  | VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY); | 
|  |  | 
|  | ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestCommandData, timedInvokeTimeoutMs)); | 
|  | ReturnErrorOnFailure(commandSender->SendCommandRequest(sessionHandle, responseTimeout)); | 
|  |  | 
|  | // If requested by the caller, provide a way to cancel the invoke interaction. | 
|  | if (outCancelFn != nullptr) | 
|  | { | 
|  | *outCancelFn = [rawDecoderPtr = decoder.get(), rawCommandSender = commandSender.get()]() { | 
|  | chip::Platform::Delete(rawCommandSender); | 
|  | chip::Platform::Delete(rawDecoderPtr); | 
|  | }; | 
|  | } | 
|  |  | 
|  | // | 
|  | // We've effectively transferred ownership of the above allocated objects to CommandSender, and we need to wait for it to call | 
|  | // us back when processing is completed (through OnDone) to eventually free up resources. | 
|  | // | 
|  | // So signal that by releasing the smart pointer. | 
|  | // | 
|  | decoder.release(); | 
|  | commandSender.release(); | 
|  |  | 
|  | return CHIP_NO_ERROR; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * A typed group command invocation function that takes as input a cluster-object representation of a command request. | 
|  | * | 
|  | * The RequestObjectT is generally expected to be a ClusterName::Commands::CommandName::Type struct, but any object | 
|  | * that can be encoded using the DataModel::Encode machinery and exposes the GetClusterId() and GetCommandId() functions | 
|  | * and a ResponseType type is expected to work. | 
|  | * | 
|  | * Since this sends a group command, no response will be received and all allocated resources will be cleared before exiting this | 
|  | * function | 
|  | */ | 
|  | template <typename RequestObjectT> | 
|  | CHIP_ERROR InvokeGroupCommandRequest(Messaging::ExchangeManager * exchangeMgr, chip::FabricIndex fabric, chip::GroupId groupId, | 
|  | const RequestObjectT & requestCommandData) | 
|  | { | 
|  | app::CommandPathParams commandPath = { groupId, RequestObjectT::GetClusterId(), RequestObjectT::GetCommandId(), | 
|  | app::CommandPathFlags::kGroupIdValid }; | 
|  | Transport::OutgoingGroupSession session(groupId, fabric); | 
|  |  | 
|  | auto commandSender = chip::Platform::MakeUnique<app::CommandSender>(nullptr, exchangeMgr); | 
|  | VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY); | 
|  |  | 
|  | ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestCommandData)); | 
|  | return commandSender->SendGroupCommandRequest(SessionHandle(session)); | 
|  | } | 
|  |  | 
|  | template <typename RequestObjectT> | 
|  | CHIP_ERROR | 
|  | InvokeCommandRequest(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId, | 
|  | const RequestObjectT & requestCommandData, | 
|  | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb, | 
|  | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb, | 
|  | uint16_t timedInvokeTimeoutMs, const Optional<System::Clock::Timeout> & responseTimeout = NullOptional) | 
|  | { | 
|  | return InvokeCommandRequest(exchangeMgr, sessionHandle, endpointId, requestCommandData, onSuccessCb, onErrorCb, | 
|  | MakeOptional(timedInvokeTimeoutMs), responseTimeout); | 
|  | } | 
|  |  | 
|  | template <typename RequestObjectT, typename std::enable_if_t<!RequestObjectT::MustUseTimedInvoke(), int> = 0> | 
|  | CHIP_ERROR | 
|  | InvokeCommandRequest(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId, | 
|  | const RequestObjectT & requestCommandData, | 
|  | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb, | 
|  | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb, | 
|  | const Optional<System::Clock::Timeout> & responseTimeout = NullOptional) | 
|  | { | 
|  | return InvokeCommandRequest(exchangeMgr, sessionHandle, endpointId, requestCommandData, onSuccessCb, onErrorCb, NullOptional, | 
|  | responseTimeout); | 
|  | } | 
|  |  | 
|  | } // namespace Controller | 
|  | } // namespace chip |