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
|