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/CommandSenderAllocator.h>
22 : #include <controller/TypedCommandCallback.h>
23 : #include <lib/core/Optional.h>
24 :
25 : namespace chip {
26 : namespace Controller {
27 :
28 : /*
29 : * A typed command invocation function that takes as input a cluster-object representation of a command request and
30 : * callbacks for success and failure and either returns a decoded cluster-object representation of the response through
31 : * the provided success callback or calls the provided failure callback.
32 : *
33 : * The RequestObjectT is generally expected to be a ClusterName::Commands::CommandName::Type struct, but any object
34 : * that can be encoded using the DataModel::Encode machinery and exposes the
35 : * GetClusterId() and GetCommandId() functions and a ResponseType type
36 : * is expected to work.
37 : *
38 : * The ResponseType is expected to be one of two things:
39 : *
40 : * - If a data response is expected on success, a struct type decodable via DataModel::Decode which has GetClusterId() and
41 : * GetCommandId() methods. A ClusterName::Commands::ResponseCommandName::DecodableType is typically used.
42 : * - If a status response is expected on success, DataModel::NullObjectType.
43 : *
44 : */
45 : template <typename RequestObjectT>
46 : CHIP_ERROR
47 0 : InvokeCommandRequest(Messaging::ExchangeManager * aExchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId,
48 : const RequestObjectT & requestCommandData,
49 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb,
50 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb,
51 : const Optional<uint16_t> & timedInvokeTimeoutMs,
52 : const Optional<System::Clock::Timeout> & responseTimeout = NullOptional)
53 : {
54 : // InvokeCommandRequest expects responses, so cannot happen over a group session.
55 0 : VerifyOrReturnError(!sessionHandle->IsGroupSession(), CHIP_ERROR_INVALID_ARGUMENT);
56 :
57 0 : app::CommandPathParams commandPath = { endpointId, 0, RequestObjectT::GetClusterId(), RequestObjectT::GetCommandId(),
58 : (app::CommandPathFlags::kEndpointIdValid) };
59 :
60 : //
61 : // 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
62 : //
63 0 : auto decoder = chip::Platform::MakeUnique<TypedCommandCallback<typename RequestObjectT::ResponseType>>(onSuccessCb, onErrorCb);
64 0 : VerifyOrReturnError(decoder != nullptr, CHIP_ERROR_NO_MEMORY);
65 :
66 : //
67 : // Upon successful completion of SendCommandRequest below, we're expected to free up the respective allocated objects
68 : // in the OnDone callback.
69 : //
70 0 : auto onDone = [rawDecoderPtr = decoder.get()](app::CommandSender * commandSender) {
71 0 : chip::Platform::Delete(commandSender);
72 0 : chip::Platform::Delete(rawDecoderPtr);
73 : };
74 :
75 0 : decoder->SetOnDoneCallback(onDone);
76 :
77 0 : auto commandSender =
78 0 : chip::Platform::MakeUnique<app::CommandSender>(decoder.get(), aExchangeMgr, timedInvokeTimeoutMs.HasValue());
79 0 : VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY);
80 :
81 0 : ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestCommandData, timedInvokeTimeoutMs));
82 0 : ReturnErrorOnFailure(commandSender->SendCommandRequest(sessionHandle, responseTimeout));
83 :
84 : //
85 : // We've effectively transferred ownership of the above allocated objects to CommandSender, and we need to wait for it to call
86 : // us back when processing is completed (through OnDone) to eventually free up resources.
87 : //
88 : // So signal that by releasing the smart pointer.
89 : //
90 0 : decoder.release();
91 0 : commandSender.release();
92 :
93 0 : return CHIP_NO_ERROR;
94 0 : }
95 :
96 : /*
97 : * A typed group command invocation function that takes as input a cluster-object representation of a command request.
98 : *
99 : * The RequestObjectT is generally expected to be a ClusterName::Commands::CommandName::Type struct, but any object
100 : * that can be encoded using the DataModel::Encode machinery and exposes the GetClusterId() and GetCommandId() functions
101 : * and a ResponseType type is expected to work.
102 : *
103 : * Since this sends a group command, no response will be received and all allocated resources will be cleared before exiting this
104 : * function
105 : */
106 : template <typename RequestObjectT>
107 : CHIP_ERROR InvokeGroupCommandRequest(Messaging::ExchangeManager * exchangeMgr, chip::FabricIndex fabric, chip::GroupId groupId,
108 : const RequestObjectT & requestCommandData)
109 : {
110 : app::CommandPathParams commandPath = { groupId, RequestObjectT::GetClusterId(), RequestObjectT::GetCommandId(),
111 : app::CommandPathFlags::kGroupIdValid };
112 : Transport::OutgoingGroupSession session(groupId, fabric);
113 :
114 : auto commandSender = chip::Platform::MakeUnique<app::CommandSender>(nullptr, exchangeMgr);
115 : VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY);
116 :
117 : ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestCommandData));
118 : return commandSender->SendGroupCommandRequest(SessionHandle(session));
119 : }
120 :
121 : template <typename RequestObjectT>
122 : CHIP_ERROR
123 : InvokeCommandRequest(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId,
124 : const RequestObjectT & requestCommandData,
125 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb,
126 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb,
127 : uint16_t timedInvokeTimeoutMs, const Optional<System::Clock::Timeout> & responseTimeout = NullOptional)
128 : {
129 : return InvokeCommandRequest(exchangeMgr, sessionHandle, endpointId, requestCommandData, onSuccessCb, onErrorCb,
130 : MakeOptional(timedInvokeTimeoutMs), responseTimeout);
131 : }
132 :
133 : template <typename RequestObjectT, typename std::enable_if_t<!RequestObjectT::MustUseTimedInvoke(), int> = 0>
134 : CHIP_ERROR
135 : InvokeCommandRequest(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId,
136 : const RequestObjectT & requestCommandData,
137 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb,
138 : typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb,
139 : const Optional<System::Clock::Timeout> & responseTimeout = NullOptional)
140 : {
141 : return InvokeCommandRequest(exchangeMgr, sessionHandle, endpointId, requestCommandData, onSuccessCb, onErrorCb, NullOptional,
142 : responseTimeout);
143 : }
144 :
145 : } // namespace Controller
146 : } // namespace chip
|