Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 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 | |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 21 | #include <controller/TypedCommandCallback.h> |
Boris Zbarsky | 70af2c4 | 2021-12-03 02:12:35 -0500 | [diff] [blame] | 22 | #include <lib/core/Optional.h> |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 23 | |
Karsten Sperling | db76ad7 | 2024-02-28 15:15:45 +1300 | [diff] [blame] | 24 | #include <functional> |
| 25 | |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 26 | namespace chip { |
| 27 | namespace Controller { |
| 28 | |
Karsten Sperling | db76ad7 | 2024-02-28 15:15:45 +1300 | [diff] [blame] | 29 | namespace Internal { |
| 30 | // Cancellation functions on InvokeCommandRequest() are for internal use only. |
| 31 | typedef std::function<void()> InvokeCancelFn; |
| 32 | } // namespace Internal |
| 33 | |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 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 |
Boris Zbarsky | 82ce9cc | 2021-11-11 15:43:06 -0500 | [diff] [blame] | 40 | * that can be encoded using the DataModel::Encode machinery and exposes the |
| 41 | * GetClusterId() and GetCommandId() functions and a ResponseType type |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 42 | * is expected to work. |
| 43 | * |
Boris Zbarsky | 82ce9cc | 2021-11-11 15:43:06 -0500 | [diff] [blame] | 44 | * The ResponseType is expected to be one of two things: |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 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. |
Boris Zbarsky | 82ce9cc | 2021-11-11 15:43:06 -0500 | [diff] [blame] | 48 | * - If a status response is expected on success, DataModel::NullObjectType. |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 49 | * |
| 50 | */ |
Boris Zbarsky | 82ce9cc | 2021-11-11 15:43:06 -0500 | [diff] [blame] | 51 | template <typename RequestObjectT> |
| 52 | CHIP_ERROR |
Marc Lepage | 9a595a4 | 2021-12-16 03:53:52 -0500 | [diff] [blame] | 53 | InvokeCommandRequest(Messaging::ExchangeManager * aExchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId, |
Boris Zbarsky | 82ce9cc | 2021-11-11 15:43:06 -0500 | [diff] [blame] | 54 | const RequestObjectT & requestCommandData, |
| 55 | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb, |
Boris Zbarsky | 70af2c4 | 2021-12-03 02:12:35 -0500 | [diff] [blame] | 56 | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb, |
C Freeman | 8ee96e4 | 2022-01-21 11:43:45 -0500 | [diff] [blame] | 57 | const Optional<uint16_t> & timedInvokeTimeoutMs, |
Karsten Sperling | db76ad7 | 2024-02-28 15:15:45 +1300 | [diff] [blame] | 58 | const Optional<System::Clock::Timeout> & responseTimeout = NullOptional, |
| 59 | Internal::InvokeCancelFn * outCancelFn = nullptr) |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 60 | { |
Boris Zbarsky | 92efde8 | 2023-02-23 11:02:13 -0500 | [diff] [blame] | 61 | // InvokeCommandRequest expects responses, so cannot happen over a group session. |
| 62 | VerifyOrReturnError(!sessionHandle->IsGroupSession(), CHIP_ERROR_INVALID_ARGUMENT); |
| 63 | |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 64 | 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 Zbarsky | 82ce9cc | 2021-11-11 15:43:06 -0500 | [diff] [blame] | 70 | auto decoder = chip::Platform::MakeUnique<TypedCommandCallback<typename RequestObjectT::ResponseType>>(onSuccessCb, onErrorCb); |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 71 | 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 Zbarsky | 70af2c4 | 2021-12-03 02:12:35 -0500 | [diff] [blame] | 84 | auto commandSender = |
| 85 | chip::Platform::MakeUnique<app::CommandSender>(decoder.get(), aExchangeMgr, timedInvokeTimeoutMs.HasValue()); |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 86 | VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY); |
| 87 | |
Boris Zbarsky | 70af2c4 | 2021-12-03 02:12:35 -0500 | [diff] [blame] | 88 | ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestCommandData, timedInvokeTimeoutMs)); |
C Freeman | 8ee96e4 | 2022-01-21 11:43:45 -0500 | [diff] [blame] | 89 | ReturnErrorOnFailure(commandSender->SendCommandRequest(sessionHandle, responseTimeout)); |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 90 | |
Karsten Sperling | db76ad7 | 2024-02-28 15:15:45 +1300 | [diff] [blame] | 91 | // 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 Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 100 | // |
Martin Turon | d24eff1 | 2021-12-17 06:21:23 -0800 | [diff] [blame] | 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. |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 103 | // |
| 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-silabs | d2f5008 | 2022-03-07 14:12:49 -0500 | [diff] [blame] | 112 | /* |
Boris Zbarsky | 92efde8 | 2023-02-23 11:02:13 -0500 | [diff] [blame] | 113 | * A typed group command invocation function that takes as input a cluster-object representation of a command request. |
mkardous-silabs | d2f5008 | 2022-03-07 14:12:49 -0500 | [diff] [blame] | 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 | * |
Boris Zbarsky | 92efde8 | 2023-02-23 11:02:13 -0500 | [diff] [blame] | 119 | * Since this sends a group command, no response will be received and all allocated resources will be cleared before exiting this |
mkardous-silabs | d2f5008 | 2022-03-07 14:12:49 -0500 | [diff] [blame] | 120 | * function |
| 121 | */ |
| 122 | template <typename RequestObjectT> |
| 123 | CHIP_ERROR InvokeGroupCommandRequest(Messaging::ExchangeManager * exchangeMgr, chip::FabricIndex fabric, chip::GroupId groupId, |
Zang MingJie | 514af46 | 2022-03-29 13:03:58 +0800 | [diff] [blame] | 124 | const RequestObjectT & requestCommandData) |
mkardous-silabs | d2f5008 | 2022-03-07 14:12:49 -0500 | [diff] [blame] | 125 | { |
mkardous-silabs | d2f5008 | 2022-03-07 14:12:49 -0500 | [diff] [blame] | 126 | app::CommandPathParams commandPath = { groupId, RequestObjectT::GetClusterId(), RequestObjectT::GetCommandId(), |
| 127 | app::CommandPathFlags::kGroupIdValid }; |
Zang MingJie | 514af46 | 2022-03-29 13:03:58 +0800 | [diff] [blame] | 128 | Transport::OutgoingGroupSession session(groupId, fabric); |
mkardous-silabs | d2f5008 | 2022-03-07 14:12:49 -0500 | [diff] [blame] | 129 | |
| 130 | auto commandSender = chip::Platform::MakeUnique<app::CommandSender>(nullptr, exchangeMgr); |
| 131 | VerifyOrReturnError(commandSender != nullptr, CHIP_ERROR_NO_MEMORY); |
| 132 | |
mkardous-silabs | 0d5c917 | 2022-05-23 16:03:02 -0400 | [diff] [blame] | 133 | ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, requestCommandData)); |
| 134 | return commandSender->SendGroupCommandRequest(SessionHandle(session)); |
mkardous-silabs | d2f5008 | 2022-03-07 14:12:49 -0500 | [diff] [blame] | 135 | } |
| 136 | |
Boris Zbarsky | 70af2c4 | 2021-12-03 02:12:35 -0500 | [diff] [blame] | 137 | template <typename RequestObjectT> |
| 138 | CHIP_ERROR |
Marc Lepage | 9a595a4 | 2021-12-16 03:53:52 -0500 | [diff] [blame] | 139 | InvokeCommandRequest(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId, |
Boris Zbarsky | 70af2c4 | 2021-12-03 02:12:35 -0500 | [diff] [blame] | 140 | const RequestObjectT & requestCommandData, |
| 141 | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb, |
| 142 | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb, |
C Freeman | 8ee96e4 | 2022-01-21 11:43:45 -0500 | [diff] [blame] | 143 | uint16_t timedInvokeTimeoutMs, const Optional<System::Clock::Timeout> & responseTimeout = NullOptional) |
Boris Zbarsky | 70af2c4 | 2021-12-03 02:12:35 -0500 | [diff] [blame] | 144 | { |
| 145 | return InvokeCommandRequest(exchangeMgr, sessionHandle, endpointId, requestCommandData, onSuccessCb, onErrorCb, |
C Freeman | 8ee96e4 | 2022-01-21 11:43:45 -0500 | [diff] [blame] | 146 | MakeOptional(timedInvokeTimeoutMs), responseTimeout); |
Boris Zbarsky | 70af2c4 | 2021-12-03 02:12:35 -0500 | [diff] [blame] | 147 | } |
| 148 | |
| 149 | template <typename RequestObjectT, typename std::enable_if_t<!RequestObjectT::MustUseTimedInvoke(), int> = 0> |
| 150 | CHIP_ERROR |
Marc Lepage | 9a595a4 | 2021-12-16 03:53:52 -0500 | [diff] [blame] | 151 | InvokeCommandRequest(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, chip::EndpointId endpointId, |
Boris Zbarsky | 70af2c4 | 2021-12-03 02:12:35 -0500 | [diff] [blame] | 152 | const RequestObjectT & requestCommandData, |
| 153 | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb, |
C Freeman | 8ee96e4 | 2022-01-21 11:43:45 -0500 | [diff] [blame] | 154 | typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb, |
| 155 | const Optional<System::Clock::Timeout> & responseTimeout = NullOptional) |
Boris Zbarsky | 70af2c4 | 2021-12-03 02:12:35 -0500 | [diff] [blame] | 156 | { |
C Freeman | 8ee96e4 | 2022-01-21 11:43:45 -0500 | [diff] [blame] | 157 | return InvokeCommandRequest(exchangeMgr, sessionHandle, endpointId, requestCommandData, onSuccessCb, onErrorCb, NullOptional, |
| 158 | responseTimeout); |
Boris Zbarsky | 70af2c4 | 2021-12-03 02:12:35 -0500 | [diff] [blame] | 159 | } |
| 160 | |
Jerry Johns | 4d08683 | 2021-10-12 16:25:10 -0700 | [diff] [blame] | 161 | } // namespace Controller |
| 162 | } // namespace chip |