Line data Source code
1 : /*
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 :
21 : #include <controller/TypedCommandCallback.h>
22 : #include <lib/core/Optional.h>
23 :
24 : #include <functional>
25 :
26 : namespace chip {
27 : namespace Controller {
28 :
29 : namespace Internal {
30 : // Cancellation functions on InvokeCommandRequest() are for internal use only.
31 : typedef std::function<void()> InvokeCancelFn;
32 : } // namespace Internal
33 :
34 : /*
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
40 : * that can be encoded using the DataModel::Encode machinery and exposes the
41 : * GetClusterId() and GetCommandId() functions and a ResponseType type
42 : * is expected to work.
43 : *
44 : * The ResponseType is expected to be one of two things:
45 : *
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.
48 : * - If a status response is expected on success, DataModel::NullObjectType.
49 : *
50 : */
51 : template <typename RequestObjectT>
52 : CHIP_ERROR
53 0 : InvokeCommandRequest(Messaging::ExchangeManager * aExchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId,
54 : const RequestObjectT & requestCommandData,
55 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb,
56 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb,
57 : const Optional<uint16_t> & timedInvokeTimeoutMs,
58 : const Optional<System::Clock::Timeout> & responseTimeout = NullOptional,
59 : Internal::InvokeCancelFn * outCancelFn = nullptr)
60 : {
61 : // InvokeCommandRequest expects responses, so cannot happen over a group session.
62 0 : VerifyOrReturnError(!sessionHandle->IsGroupSession(), CHIP_ERROR_INVALID_ARGUMENT);
63 :
64 0 : 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 : //
70 0 : auto decoder = chip::Platform::MakeUnique<TypedCommandCallback<typename RequestObjectT::ResponseType>>(onSuccessCb, onErrorCb);
71 0 : 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 0 : auto onDone = [rawDecoderPtr = decoder.get()](app::CommandSender * commandSender) {
78 0 : chip::Platform::Delete(commandSender);
79 0 : chip::Platform::Delete(rawDecoderPtr);
80 : };
81 :
82 0 : decoder->SetOnDoneCallback(onDone);
83 :
84 0 : auto commandSender =
85 0 : chip::Platform::MakeUnique<app::CommandSender>(decoder.get(), aExchangeMgr, timedInvokeTimeoutMs.HasValue());
86 0 : VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY);
87 :
88 0 : ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestCommandData, timedInvokeTimeoutMs));
89 0 : ReturnErrorOnFailure(commandSender->SendCommandRequest(sessionHandle, responseTimeout));
90 :
91 : // If requested by the caller, provide a way to cancel the invoke interaction.
92 0 : if (outCancelFn != nullptr)
93 : {
94 0 : *outCancelFn = [rawDecoderPtr = decoder.get(), rawCommandSender = commandSender.get()]() {
95 0 : chip::Platform::Delete(rawCommandSender);
96 0 : chip::Platform::Delete(rawDecoderPtr);
97 : };
98 : }
99 :
100 : //
101 : // 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.
103 : //
104 : // So signal that by releasing the smart pointer.
105 : //
106 0 : decoder.release();
107 0 : commandSender.release();
108 :
109 0 : return CHIP_NO_ERROR;
110 0 : }
111 :
112 : /*
113 : * A typed group command invocation function that takes as input a cluster-object representation of a command request.
114 : *
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 : *
119 : * Since this sends a group command, no response will be received and all allocated resources will be cleared before exiting this
120 : * function
121 : */
122 : template <typename RequestObjectT>
123 : CHIP_ERROR InvokeGroupCommandRequest(Messaging::ExchangeManager * exchangeMgr, chip::FabricIndex fabric, chip::GroupId groupId,
124 : const RequestObjectT & requestCommandData)
125 : {
126 : app::CommandPathParams commandPath = { groupId, RequestObjectT::GetClusterId(), RequestObjectT::GetCommandId(),
127 : app::CommandPathFlags::kGroupIdValid };
128 : Transport::OutgoingGroupSession session(groupId, fabric);
129 :
130 : auto commandSender = chip::Platform::MakeUnique<app::CommandSender>(nullptr, exchangeMgr);
131 : VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY);
132 :
133 : ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestCommandData));
134 : return commandSender->SendGroupCommandRequest(SessionHandle(session));
135 : }
136 :
137 : template <typename RequestObjectT>
138 : CHIP_ERROR
139 : InvokeCommandRequest(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId,
140 : const RequestObjectT & requestCommandData,
141 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb,
142 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb,
143 : uint16_t timedInvokeTimeoutMs, const Optional<System::Clock::Timeout> & responseTimeout = NullOptional)
144 : {
145 : return InvokeCommandRequest(exchangeMgr, sessionHandle, endpointId, requestCommandData, onSuccessCb, onErrorCb,
146 : MakeOptional(timedInvokeTimeoutMs), responseTimeout);
147 : }
148 :
149 : template <typename RequestObjectT, typename std::enable_if_t<!RequestObjectT::MustUseTimedInvoke(), int> = 0>
150 : CHIP_ERROR
151 0 : InvokeCommandRequest(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId,
152 : const RequestObjectT & requestCommandData,
153 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb,
154 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb,
155 : const Optional<System::Clock::Timeout> & responseTimeout = NullOptional)
156 : {
157 0 : return InvokeCommandRequest(exchangeMgr, sessionHandle, endpointId, requestCommandData, onSuccessCb, onErrorCb, NullOptional,
158 0 : responseTimeout);
159 : }
160 :
161 : } // namespace Controller
162 : } // namespace chip
|