| /* |
| * |
| * 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 |