| /* |
| * |
| * 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 "protocols/interaction_model/Constants.h" |
| #include <app/CommandSender.h> |
| #include <app/data-model/Decode.h> |
| #include <app/data-model/NullObject.h> |
| #include <functional> |
| |
| namespace chip { |
| namespace Controller { |
| |
| /* |
| * This provides an adapter class that implements CommandSender::Callback and provides two additional features: |
| * 1. The ability to pass in std::function closures to permit more flexible programming scenarios than are provided by the strict |
| * delegate interface stipulated by CommandSender::Callback |
| * |
| * 2. Automatic decoding of command response data provided in the TLVReader by the CommandSender callback into a decoded cluster |
| * object. |
| */ |
| template <typename CommandResponseObjectT> |
| class TypedCommandCallback final : public app::CommandSender::Callback |
| { |
| public: |
| using OnSuccessCallbackType = |
| std::function<void(const app::ConcreteCommandPath &, const app::StatusIB &, const CommandResponseObjectT &)>; |
| using OnErrorCallbackType = std::function<void(CHIP_ERROR aError)>; |
| using OnDoneCallbackType = std::function<void(app::CommandSender * commandSender)>; |
| |
| /* |
| * Constructor that takes in success, failure and onDone callbacks. |
| * |
| * The latter can be provided later through the SetOnDoneCallback below in cases where the |
| * TypedCommandCallback object needs to be created first before it can be passed in as a closure |
| * into a hypothetical OnDoneCallback function. |
| */ |
| TypedCommandCallback(OnSuccessCallbackType aOnSuccess, OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone = {}) : |
| mOnSuccess(aOnSuccess), mOnError(aOnError), mOnDone(aOnDone) |
| {} |
| |
| void SetOnDoneCallback(OnDoneCallbackType callback) { mOnDone = callback; } |
| |
| private: |
| void OnResponse(app::CommandSender * apCommandSender, const app::ConcreteCommandPath & aCommandPath, |
| const app::StatusIB & aStatus, TLV::TLVReader * aReader) override; |
| |
| void OnError(const app::CommandSender * apCommandSender, CHIP_ERROR aError) override |
| { |
| if (mCalledCallback) |
| { |
| return; |
| } |
| mCalledCallback = true; |
| |
| mOnError(aError); |
| } |
| |
| void OnDone(app::CommandSender * apCommandSender) override { mOnDone(apCommandSender); } |
| |
| OnSuccessCallbackType mOnSuccess; |
| OnErrorCallbackType mOnError; |
| OnDoneCallbackType mOnDone; |
| |
| bool mCalledCallback = false; |
| }; |
| |
| /* |
| * Decodes the data provided by the TLVReader into the templated cluster object that denotes the command response. |
| * |
| * This function specifically decodes command responses that have actual data payloads. |
| */ |
| template <typename CommandResponseObjectT> |
| void TypedCommandCallback<CommandResponseObjectT>::OnResponse(app::CommandSender * apCommandSender, |
| const app::ConcreteCommandPath & aCommandPath, |
| const app::StatusIB & aStatus, TLV::TLVReader * aReader) |
| { |
| if (mCalledCallback) |
| { |
| return; |
| } |
| mCalledCallback = true; |
| |
| CommandResponseObjectT response; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| // |
| // We're expecting response data in this variant of OnResponse. Consequently, aReader should always be |
| // non-null. If it is, it means we received a success status code instead, which is not what was expected. |
| // |
| VerifyOrExit(aReader != nullptr, err = CHIP_ERROR_SCHEMA_MISMATCH); |
| |
| // |
| // Validate that the data response we received matches what we expect in terms of its cluster and command IDs. |
| // |
| VerifyOrExit(aCommandPath.mClusterId == CommandResponseObjectT::GetClusterId() && |
| aCommandPath.mCommandId == CommandResponseObjectT::GetCommandId(), |
| err = CHIP_ERROR_SCHEMA_MISMATCH); |
| |
| err = app::DataModel::Decode(*aReader, response); |
| SuccessOrExit(err); |
| |
| mOnSuccess(aCommandPath, aStatus, response); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| mOnError(err); |
| } |
| } |
| |
| /* |
| * Decodes the data provided by the TLVReader into the templated cluster object that denotes the command response. |
| * |
| * This function specifically decodes command responses that do not have actual data payloads and where the passed in TLVReader |
| * should be null. |
| */ |
| template <> |
| inline void TypedCommandCallback<app::DataModel::NullObjectType>::OnResponse(app::CommandSender * apCommandSender, |
| const app::ConcreteCommandPath & aCommandPath, |
| const app::StatusIB & aStatus, |
| TLV::TLVReader * aReader) |
| { |
| if (mCalledCallback) |
| { |
| return; |
| } |
| mCalledCallback = true; |
| |
| // |
| // If we got a valid reader, it means we received response data that we were not expecting to receive. |
| // |
| if (aReader != nullptr) |
| { |
| mOnError(CHIP_ERROR_SCHEMA_MISMATCH); |
| return; |
| } |
| |
| app::DataModel::NullObjectType nullResp; |
| mOnSuccess(aCommandPath, aStatus, nullResp); |
| } |
| |
| } // namespace Controller |
| } // namespace chip |