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 "protocols/interaction_model/Constants.h" 22 : #include <app/CommandSender.h> 23 : #include <app/data-model/Decode.h> 24 : #include <app/data-model/NullObject.h> 25 : #include <functional> 26 : 27 : namespace chip { 28 : namespace Controller { 29 : 30 : /* 31 : * This provides an adapter class that implements CommandSender::Callback and provides two additional features: 32 : * 1. The ability to pass in std::function closures to permit more flexible programming scenarios than are provided by the strict 33 : * delegate interface stipulated by CommandSender::Callback 34 : * 35 : * 2. Automatic decoding of command response data provided in the TLVReader by the CommandSender callback into a decoded cluster 36 : * object. 37 : */ 38 : template <typename CommandResponseObjectT> 39 : class TypedCommandCallback final : public app::CommandSender::Callback 40 : { 41 : public: 42 : using OnSuccessCallbackType = 43 : std::function<void(const app::ConcreteCommandPath &, const app::StatusIB &, const CommandResponseObjectT &)>; 44 : using OnErrorCallbackType = std::function<void(CHIP_ERROR aError)>; 45 : using OnDoneCallbackType = std::function<void(app::CommandSender * commandSender)>; 46 : 47 : /* 48 : * Constructor that takes in success, failure and onDone callbacks. 49 : * 50 : * The latter can be provided later through the SetOnDoneCallback below in cases where the 51 : * TypedCommandCallback object needs to be created first before it can be passed in as a closure 52 : * into a hypothetical OnDoneCallback function. 53 : */ 54 0 : TypedCommandCallback(OnSuccessCallbackType aOnSuccess, OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone = {}) : 55 0 : mOnSuccess(aOnSuccess), mOnError(aOnError), mOnDone(aOnDone) 56 0 : {} 57 : 58 0 : void SetOnDoneCallback(OnDoneCallbackType callback) { mOnDone = callback; } 59 : 60 : private: 61 : void OnResponse(app::CommandSender * apCommandSender, const app::ConcreteCommandPath & aCommandPath, 62 : const app::StatusIB & aStatus, TLV::TLVReader * aReader) override; 63 : 64 0 : void OnError(const app::CommandSender * apCommandSender, CHIP_ERROR aError) override 65 : { 66 0 : if (mCalledCallback) 67 : { 68 0 : return; 69 : } 70 0 : mCalledCallback = true; 71 : 72 0 : mOnError(aError); 73 : } 74 : 75 0 : void OnDone(app::CommandSender * apCommandSender) override 76 : { 77 0 : if (!mCalledCallback) 78 : { 79 : // This can happen if the server sends a response with an empty 80 : // InvokeResponses list. Since we are not sending wildcard command 81 : // paths, that's not a valid response and we should treat it as an 82 : // error. Use the error we would have gotten if we in fact expected 83 : // a nonempty list. 84 0 : OnError(apCommandSender, CHIP_END_OF_TLV); 85 : } 86 : 87 0 : mOnDone(apCommandSender); 88 0 : } 89 : 90 : OnSuccessCallbackType mOnSuccess; 91 : OnErrorCallbackType mOnError; 92 : OnDoneCallbackType mOnDone; 93 : 94 : bool mCalledCallback = false; 95 : }; 96 : 97 : /* 98 : * Decodes the data provided by the TLVReader into the templated cluster object that denotes the command response. 99 : * 100 : * This function specifically decodes command responses that have actual data payloads. 101 : */ 102 : template <typename CommandResponseObjectT> 103 0 : void TypedCommandCallback<CommandResponseObjectT>::OnResponse(app::CommandSender * apCommandSender, 104 : const app::ConcreteCommandPath & aCommandPath, 105 : const app::StatusIB & aStatus, TLV::TLVReader * aReader) 106 : { 107 0 : if (mCalledCallback) 108 : { 109 0 : return; 110 : } 111 0 : mCalledCallback = true; 112 : 113 0 : CommandResponseObjectT response; 114 0 : CHIP_ERROR err = CHIP_NO_ERROR; 115 : 116 : // 117 : // We're expecting response data in this variant of OnResponse. Consequently, aReader should always be 118 : // non-null. If it is, it means we received a success status code instead, which is not what was expected. 119 : // 120 0 : VerifyOrExit(aReader != nullptr, err = CHIP_ERROR_SCHEMA_MISMATCH); 121 : 122 : // 123 : // Validate that the data response we received matches what we expect in terms of its cluster and command IDs. 124 : // 125 0 : VerifyOrExit(aCommandPath.mClusterId == CommandResponseObjectT::GetClusterId() && 126 : aCommandPath.mCommandId == CommandResponseObjectT::GetCommandId(), 127 : err = CHIP_ERROR_SCHEMA_MISMATCH); 128 : 129 0 : err = app::DataModel::Decode(*aReader, response); 130 0 : SuccessOrExit(err); 131 : 132 0 : mOnSuccess(aCommandPath, aStatus, response); 133 : 134 0 : exit: 135 0 : if (err != CHIP_NO_ERROR) 136 : { 137 0 : mOnError(err); 138 : } 139 0 : } 140 : 141 : /* 142 : * Decodes the data provided by the TLVReader into the templated cluster object that denotes the command response. 143 : * 144 : * This function specifically decodes command responses that do not have actual data payloads and where the passed in TLVReader 145 : * should be null. 146 : */ 147 : template <> 148 0 : inline void TypedCommandCallback<app::DataModel::NullObjectType>::OnResponse(app::CommandSender * apCommandSender, 149 : const app::ConcreteCommandPath & aCommandPath, 150 : const app::StatusIB & aStatus, 151 : TLV::TLVReader * aReader) 152 : { 153 0 : if (mCalledCallback) 154 : { 155 0 : return; 156 : } 157 0 : mCalledCallback = true; 158 : 159 : // 160 : // If we got a valid reader, it means we received response data that we were not expecting to receive. 161 : // 162 0 : if (aReader != nullptr) 163 : { 164 0 : mOnError(CHIP_ERROR_SCHEMA_MISMATCH); 165 0 : return; 166 : } 167 : 168 : app::DataModel::NullObjectType nullResp; 169 0 : mOnSuccess(aCommandPath, aStatus, nullResp); 170 : } 171 : 172 : } // namespace Controller 173 : } // namespace chip