blob: 7576f8aac2e78b6690963c2299087d211d22ea54 [file] [log] [blame]
/*
*
* 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