blob: 7576f8aac2e78b6690963c2299087d211d22ea54 [file] [log] [blame]
Jerry Johns4d086832021-10-12 16:25:10 -07001/*
2 *
3 * Copyright (c) 2021 Project CHIP Authors
4 * All rights reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#pragma once
20
Jerry Johns4d086832021-10-12 16:25:10 -070021#include <controller/TypedCommandCallback.h>
Boris Zbarsky70af2c42021-12-03 02:12:35 -050022#include <lib/core/Optional.h>
Jerry Johns4d086832021-10-12 16:25:10 -070023
Karsten Sperlingdb76ad72024-02-28 15:15:45 +130024#include <functional>
25
Jerry Johns4d086832021-10-12 16:25:10 -070026namespace chip {
27namespace Controller {
28
Karsten Sperlingdb76ad72024-02-28 15:15:45 +130029namespace Internal {
30// Cancellation functions on InvokeCommandRequest() are for internal use only.
31typedef std::function<void()> InvokeCancelFn;
32} // namespace Internal
33
Jerry Johns4d086832021-10-12 16:25:10 -070034/*
35 * A typed command invocation function that takes as input a cluster-object representation of a command request and
36 * callbacks for success and failure and either returns a decoded cluster-object representation of the response through
37 * the provided success callback or calls the provided failure callback.
38 *
39 * The RequestObjectT is generally expected to be a ClusterName::Commands::CommandName::Type struct, but any object
Boris Zbarsky82ce9cc2021-11-11 15:43:06 -050040 * that can be encoded using the DataModel::Encode machinery and exposes the
41 * GetClusterId() and GetCommandId() functions and a ResponseType type
Jerry Johns4d086832021-10-12 16:25:10 -070042 * is expected to work.
43 *
Boris Zbarsky82ce9cc2021-11-11 15:43:06 -050044 * The ResponseType is expected to be one of two things:
Jerry Johns4d086832021-10-12 16:25:10 -070045 *
46 * - If a data response is expected on success, a struct type decodable via DataModel::Decode which has GetClusterId() and
47 * GetCommandId() methods. A ClusterName::Commands::ResponseCommandName::DecodableType is typically used.
Boris Zbarsky82ce9cc2021-11-11 15:43:06 -050048 * - If a status response is expected on success, DataModel::NullObjectType.
Jerry Johns4d086832021-10-12 16:25:10 -070049 *
50 */
Boris Zbarsky82ce9cc2021-11-11 15:43:06 -050051template <typename RequestObjectT>
52CHIP_ERROR
Marc Lepage9a595a42021-12-16 03:53:52 -050053InvokeCommandRequest(Messaging::ExchangeManager * aExchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId,
Boris Zbarsky82ce9cc2021-11-11 15:43:06 -050054 const RequestObjectT & requestCommandData,
55 typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb,
Boris Zbarsky70af2c42021-12-03 02:12:35 -050056 typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb,
C Freeman8ee96e42022-01-21 11:43:45 -050057 const Optional<uint16_t> & timedInvokeTimeoutMs,
Karsten Sperlingdb76ad72024-02-28 15:15:45 +130058 const Optional<System::Clock::Timeout> & responseTimeout = NullOptional,
59 Internal::InvokeCancelFn * outCancelFn = nullptr)
Jerry Johns4d086832021-10-12 16:25:10 -070060{
Boris Zbarsky92efde82023-02-23 11:02:13 -050061 // InvokeCommandRequest expects responses, so cannot happen over a group session.
62 VerifyOrReturnError(!sessionHandle->IsGroupSession(), CHIP_ERROR_INVALID_ARGUMENT);
63
Jerry Johns4d086832021-10-12 16:25:10 -070064 app::CommandPathParams commandPath = { endpointId, 0, RequestObjectT::GetClusterId(), RequestObjectT::GetCommandId(),
65 (app::CommandPathFlags::kEndpointIdValid) };
66
67 //
68 // 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
69 //
Boris Zbarsky82ce9cc2021-11-11 15:43:06 -050070 auto decoder = chip::Platform::MakeUnique<TypedCommandCallback<typename RequestObjectT::ResponseType>>(onSuccessCb, onErrorCb);
Jerry Johns4d086832021-10-12 16:25:10 -070071 VerifyOrReturnError(decoder != nullptr, CHIP_ERROR_NO_MEMORY);
72
73 //
74 // Upon successful completion of SendCommandRequest below, we're expected to free up the respective allocated objects
75 // in the OnDone callback.
76 //
77 auto onDone = [rawDecoderPtr = decoder.get()](app::CommandSender * commandSender) {
78 chip::Platform::Delete(commandSender);
79 chip::Platform::Delete(rawDecoderPtr);
80 };
81
82 decoder->SetOnDoneCallback(onDone);
83
Boris Zbarsky70af2c42021-12-03 02:12:35 -050084 auto commandSender =
85 chip::Platform::MakeUnique<app::CommandSender>(decoder.get(), aExchangeMgr, timedInvokeTimeoutMs.HasValue());
Jerry Johns4d086832021-10-12 16:25:10 -070086 VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY);
87
Boris Zbarsky70af2c42021-12-03 02:12:35 -050088 ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestCommandData, timedInvokeTimeoutMs));
C Freeman8ee96e42022-01-21 11:43:45 -050089 ReturnErrorOnFailure(commandSender->SendCommandRequest(sessionHandle, responseTimeout));
Jerry Johns4d086832021-10-12 16:25:10 -070090
Karsten Sperlingdb76ad72024-02-28 15:15:45 +130091 // If requested by the caller, provide a way to cancel the invoke interaction.
92 if (outCancelFn != nullptr)
93 {
94 *outCancelFn = [rawDecoderPtr = decoder.get(), rawCommandSender = commandSender.get()]() {
95 chip::Platform::Delete(rawCommandSender);
96 chip::Platform::Delete(rawDecoderPtr);
97 };
98 }
99
Jerry Johns4d086832021-10-12 16:25:10 -0700100 //
Martin Turond24eff12021-12-17 06:21:23 -0800101 // We've effectively transferred ownership of the above allocated objects to CommandSender, and we need to wait for it to call
102 // us back when processing is completed (through OnDone) to eventually free up resources.
Jerry Johns4d086832021-10-12 16:25:10 -0700103 //
104 // So signal that by releasing the smart pointer.
105 //
106 decoder.release();
107 commandSender.release();
108
109 return CHIP_NO_ERROR;
110}
111
mkardous-silabsd2f50082022-03-07 14:12:49 -0500112/*
Boris Zbarsky92efde82023-02-23 11:02:13 -0500113 * A typed group command invocation function that takes as input a cluster-object representation of a command request.
mkardous-silabsd2f50082022-03-07 14:12:49 -0500114 *
115 * The RequestObjectT is generally expected to be a ClusterName::Commands::CommandName::Type struct, but any object
116 * that can be encoded using the DataModel::Encode machinery and exposes the GetClusterId() and GetCommandId() functions
117 * and a ResponseType type is expected to work.
118 *
Boris Zbarsky92efde82023-02-23 11:02:13 -0500119 * Since this sends a group command, no response will be received and all allocated resources will be cleared before exiting this
mkardous-silabsd2f50082022-03-07 14:12:49 -0500120 * function
121 */
122template <typename RequestObjectT>
123CHIP_ERROR InvokeGroupCommandRequest(Messaging::ExchangeManager * exchangeMgr, chip::FabricIndex fabric, chip::GroupId groupId,
Zang MingJie514af462022-03-29 13:03:58 +0800124 const RequestObjectT & requestCommandData)
mkardous-silabsd2f50082022-03-07 14:12:49 -0500125{
mkardous-silabsd2f50082022-03-07 14:12:49 -0500126 app::CommandPathParams commandPath = { groupId, RequestObjectT::GetClusterId(), RequestObjectT::GetCommandId(),
127 app::CommandPathFlags::kGroupIdValid };
Zang MingJie514af462022-03-29 13:03:58 +0800128 Transport::OutgoingGroupSession session(groupId, fabric);
mkardous-silabsd2f50082022-03-07 14:12:49 -0500129
130 auto commandSender = chip::Platform::MakeUnique<app::CommandSender>(nullptr, exchangeMgr);
131 VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY);
132
mkardous-silabs0d5c9172022-05-23 16:03:02 -0400133 ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestCommandData));
134 return commandSender->SendGroupCommandRequest(SessionHandle(session));
mkardous-silabsd2f50082022-03-07 14:12:49 -0500135}
136
Boris Zbarsky70af2c42021-12-03 02:12:35 -0500137template <typename RequestObjectT>
138CHIP_ERROR
Marc Lepage9a595a42021-12-16 03:53:52 -0500139InvokeCommandRequest(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId,
Boris Zbarsky70af2c42021-12-03 02:12:35 -0500140 const RequestObjectT & requestCommandData,
141 typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb,
142 typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb,
C Freeman8ee96e42022-01-21 11:43:45 -0500143 uint16_t timedInvokeTimeoutMs, const Optional<System::Clock::Timeout> & responseTimeout = NullOptional)
Boris Zbarsky70af2c42021-12-03 02:12:35 -0500144{
145 return InvokeCommandRequest(exchangeMgr, sessionHandle, endpointId, requestCommandData, onSuccessCb, onErrorCb,
C Freeman8ee96e42022-01-21 11:43:45 -0500146 MakeOptional(timedInvokeTimeoutMs), responseTimeout);
Boris Zbarsky70af2c42021-12-03 02:12:35 -0500147}
148
149template <typename RequestObjectT, typename std::enable_if_t<!RequestObjectT::MustUseTimedInvoke(), int> = 0>
150CHIP_ERROR
Marc Lepage9a595a42021-12-16 03:53:52 -0500151InvokeCommandRequest(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId,
Boris Zbarsky70af2c42021-12-03 02:12:35 -0500152 const RequestObjectT & requestCommandData,
153 typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb,
C Freeman8ee96e42022-01-21 11:43:45 -0500154 typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb,
155 const Optional<System::Clock::Timeout> & responseTimeout = NullOptional)
Boris Zbarsky70af2c42021-12-03 02:12:35 -0500156{
C Freeman8ee96e42022-01-21 11:43:45 -0500157 return InvokeCommandRequest(exchangeMgr, sessionHandle, endpointId, requestCommandData, onSuccessCb, onErrorCb, NullOptional,
158 responseTimeout);
Boris Zbarsky70af2c42021-12-03 02:12:35 -0500159}
160
Jerry Johns4d086832021-10-12 16:25:10 -0700161} // namespace Controller
162} // namespace chip