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 <app/AppConfig.h>
22 : #include <app/BufferedReadCallback.h>
23 : #include <app/ConcreteAttributePath.h>
24 : #include <app/data-model/Decode.h>
25 : #include <functional>
26 : #include <lib/support/CHIPMem.h>
27 :
28 : #if CHIP_CONFIG_ENABLE_READ_CLIENT
29 : namespace chip {
30 : namespace Controller {
31 :
32 : /*
33 : * This provides an adapter class that implements ReadClient::Callback and provides three additional
34 : * features:
35 : * 1. The ability to pass in std::function closures to permit more flexible
36 : * programming scenarios than are provided by the strict delegate interface
37 : * stipulated by ReadClient::Callback.
38 : *
39 : * 2. Automatic decoding of attribute data provided in the TLVReader by
40 : * ReadClient::Callback::OnAttributeData into a decoded cluster object.
41 : *
42 : * 3. Automatically representing all errors as a CHIP_ERROR (which might
43 : * encapsulate a StatusIB). This could be a path-specific error or it
44 : * could be a general error for the entire request; the distinction is not
45 : * that important, because we only have one path involved. If the
46 : * CHIP_ERROR encapsulates a StatusIB, StatusIB::InitFromChipError can be
47 : * used to extract the status.
48 : */
49 : template <typename DecodableAttributeType>
50 : class TypedReadAttributeCallback final : public app::ReadClient::Callback
51 : {
52 : public:
53 : using OnSuccessCallbackType =
54 : std::function<void(const app::ConcreteDataAttributePath & aPath, const DecodableAttributeType & aData)>;
55 : using OnErrorCallbackType = std::function<void(const app::ConcreteDataAttributePath * aPath, CHIP_ERROR aError)>;
56 : using OnDoneCallbackType = std::function<void(TypedReadAttributeCallback * callback)>;
57 : using OnSubscriptionEstablishedCallbackType =
58 : std::function<void(const app::ReadClient & readClient, SubscriptionId aSubscriptionId)>;
59 : using OnResubscriptionAttemptCallbackType =
60 : std::function<void(const app::ReadClient & readClient, CHIP_ERROR aError, uint32_t aNextResubscribeIntervalMsec)>;
61 0 : TypedReadAttributeCallback(ClusterId aClusterId, AttributeId aAttributeId, OnSuccessCallbackType aOnSuccess,
62 : OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone,
63 : OnSubscriptionEstablishedCallbackType aOnSubscriptionEstablished = nullptr,
64 : OnResubscriptionAttemptCallbackType aOnResubscriptionAttempt = nullptr) :
65 0 : mClusterId(aClusterId),
66 0 : mAttributeId(aAttributeId), mOnSuccess(aOnSuccess), mOnError(aOnError), mOnDone(aOnDone),
67 0 : mOnSubscriptionEstablished(aOnSubscriptionEstablished), mOnResubscriptionAttempt(aOnResubscriptionAttempt),
68 0 : mBufferedReadAdapter(*this)
69 0 : {}
70 :
71 0 : ~TypedReadAttributeCallback()
72 : {
73 : // Ensure we release the ReadClient before we tear down anything else,
74 : // so it can call our OnDeallocatePaths properly.
75 0 : mReadClient = nullptr;
76 0 : }
77 :
78 0 : app::BufferedReadCallback & GetBufferedCallback() { return mBufferedReadAdapter; }
79 :
80 0 : void AdoptReadClient(Platform::UniquePtr<app::ReadClient> aReadClient) { mReadClient = std::move(aReadClient); }
81 :
82 : private:
83 0 : void OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
84 : const app::StatusIB & aStatus) override
85 : {
86 0 : if (mCalledCallback && mReadClient->IsReadType())
87 : {
88 0 : return;
89 : }
90 0 : mCalledCallback = true;
91 :
92 0 : CHIP_ERROR err = CHIP_NO_ERROR;
93 : DecodableAttributeType value;
94 :
95 : //
96 : // We shouldn't be getting list item operations in the provided path since that should be handled by the buffered read
97 : // callback. If we do, that's a bug.
98 : //
99 0 : VerifyOrDie(!aPath.IsListItemOperation());
100 :
101 0 : VerifyOrExit(aStatus.IsSuccess(), err = aStatus.ToChipError());
102 0 : VerifyOrExit(aPath.mClusterId == mClusterId && aPath.mAttributeId == mAttributeId, err = CHIP_ERROR_SCHEMA_MISMATCH);
103 0 : VerifyOrExit(apData != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
104 :
105 0 : SuccessOrExit(err = app::DataModel::Decode(*apData, value));
106 :
107 0 : mOnSuccess(aPath, value);
108 :
109 0 : exit:
110 0 : if (err != CHIP_NO_ERROR)
111 : {
112 0 : mOnError(&aPath, err);
113 : }
114 : }
115 :
116 0 : void OnError(CHIP_ERROR aError) override
117 : {
118 0 : if (mCalledCallback && mReadClient->IsReadType())
119 : {
120 0 : return;
121 : }
122 0 : mCalledCallback = true;
123 :
124 0 : mOnError(nullptr, aError);
125 : }
126 :
127 0 : void OnDone(app::ReadClient *) override { mOnDone(this); }
128 :
129 0 : void OnSubscriptionEstablished(SubscriptionId aSubscriptionId) override
130 : {
131 0 : if (mOnSubscriptionEstablished)
132 : {
133 0 : mOnSubscriptionEstablished(*mReadClient.get(), aSubscriptionId);
134 : }
135 0 : }
136 :
137 0 : CHIP_ERROR OnResubscriptionNeeded(chip::app::ReadClient * apReadClient, CHIP_ERROR aTerminationCause) override
138 : {
139 0 : ReturnErrorOnFailure(app::ReadClient::Callback::OnResubscriptionNeeded(apReadClient, aTerminationCause));
140 :
141 0 : if (mOnResubscriptionAttempt)
142 : {
143 0 : mOnResubscriptionAttempt(*mReadClient.get(), aTerminationCause, apReadClient->ComputeTimeTillNextSubscription());
144 : }
145 :
146 0 : return CHIP_NO_ERROR;
147 : }
148 :
149 0 : void OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) override
150 : {
151 0 : VerifyOrDie(aReadPrepareParams.mAttributePathParamsListSize == 1 &&
152 : aReadPrepareParams.mpAttributePathParamsList != nullptr);
153 0 : chip::Platform::Delete<app::AttributePathParams>(aReadPrepareParams.mpAttributePathParamsList);
154 :
155 0 : if (aReadPrepareParams.mDataVersionFilterListSize == 1 && aReadPrepareParams.mpDataVersionFilterList != nullptr)
156 : {
157 0 : chip::Platform::Delete<app::DataVersionFilter>(aReadPrepareParams.mpDataVersionFilterList);
158 : }
159 0 : }
160 :
161 : ClusterId mClusterId;
162 : AttributeId mAttributeId;
163 : OnSuccessCallbackType mOnSuccess;
164 : OnErrorCallbackType mOnError;
165 : OnDoneCallbackType mOnDone;
166 : OnSubscriptionEstablishedCallbackType mOnSubscriptionEstablished;
167 : OnResubscriptionAttemptCallbackType mOnResubscriptionAttempt;
168 : app::BufferedReadCallback mBufferedReadAdapter;
169 : Platform::UniquePtr<app::ReadClient> mReadClient;
170 : // For reads, we ensure that we make only one data/error callback to our consumer.
171 : bool mCalledCallback = false;
172 : };
173 :
174 : template <typename DecodableEventType>
175 : class TypedReadEventCallback final : public app::ReadClient::Callback
176 : {
177 : public:
178 : using OnSuccessCallbackType = std::function<void(const app::EventHeader & aEventHeader, const DecodableEventType & aData)>;
179 : using OnErrorCallbackType = std::function<void(const app::EventHeader * apEventHeader, CHIP_ERROR aError)>;
180 : using OnDoneCallbackType = std::function<void(app::ReadClient * apReadClient)>;
181 : using OnSubscriptionEstablishedCallbackType =
182 : std::function<void(const app::ReadClient & aReadClient, SubscriptionId aSubscriptionId)>;
183 : using OnResubscriptionAttemptCallbackType =
184 : std::function<void(const app::ReadClient & aReadClient, CHIP_ERROR aError, uint32_t aNextResubscribeIntervalMsec)>;
185 :
186 : TypedReadEventCallback(OnSuccessCallbackType aOnSuccess, OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone,
187 : OnSubscriptionEstablishedCallbackType aOnSubscriptionEstablished = nullptr,
188 : OnResubscriptionAttemptCallbackType aOnResubscriptionAttempt = nullptr) :
189 : mOnSuccess(aOnSuccess),
190 : mOnError(aOnError), mOnDone(aOnDone), mOnSubscriptionEstablished(aOnSubscriptionEstablished),
191 : mOnResubscriptionAttempt(aOnResubscriptionAttempt)
192 : {}
193 :
194 : ~TypedReadEventCallback()
195 : {
196 : // Ensure we release the ReadClient before we tear down anything else,
197 : // so it can call our OnDeallocatePaths properly.
198 : mReadClient = nullptr;
199 : }
200 :
201 : void AdoptReadClient(Platform::UniquePtr<app::ReadClient> aReadClient) { mReadClient = std::move(aReadClient); }
202 :
203 : private:
204 : void OnEventData(const app::EventHeader & aEventHeader, TLV::TLVReader * apData, const app::StatusIB * apStatus) override
205 : {
206 : if (mCalledCallback && mReadClient->IsReadType())
207 : {
208 : return;
209 : }
210 : mCalledCallback = true;
211 :
212 : CHIP_ERROR err = CHIP_NO_ERROR;
213 : DecodableEventType value;
214 :
215 : // Only one of the apData and apStatus can be non-null, so apStatus will always indicate a failure status when it is not
216 : // nullptr.
217 : VerifyOrExit(apStatus == nullptr, err = apStatus->ToChipError());
218 :
219 : VerifyOrExit(apData != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
220 :
221 : VerifyOrExit((aEventHeader.mPath.mEventId == value.GetEventId()) && (aEventHeader.mPath.mClusterId == value.GetClusterId()),
222 : CHIP_ERROR_SCHEMA_MISMATCH);
223 : err = app::DataModel::Decode(*apData, value);
224 : SuccessOrExit(err);
225 :
226 : mOnSuccess(aEventHeader, value);
227 :
228 : exit:
229 : if (err != CHIP_NO_ERROR)
230 : {
231 : mOnError(&aEventHeader, err);
232 : }
233 : }
234 :
235 : void OnError(CHIP_ERROR aError) override
236 : {
237 : if (mCalledCallback && mReadClient->IsReadType())
238 : {
239 : return;
240 : }
241 : mCalledCallback = true;
242 :
243 : mOnError(nullptr, aError);
244 : }
245 :
246 : void OnDone(app::ReadClient * apReadClient) override
247 : {
248 : if (mOnDone != nullptr)
249 : {
250 : mOnDone(apReadClient);
251 : }
252 :
253 : // Always needs to be the last call
254 : chip::Platform::Delete(this);
255 : }
256 :
257 : void OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) override
258 : {
259 : VerifyOrDie(aReadPrepareParams.mEventPathParamsListSize == 1 && aReadPrepareParams.mpEventPathParamsList != nullptr);
260 : chip::Platform::Delete<app::EventPathParams>(aReadPrepareParams.mpEventPathParamsList);
261 : }
262 :
263 : void OnSubscriptionEstablished(SubscriptionId aSubscriptionId) override
264 : {
265 : if (mOnSubscriptionEstablished)
266 : {
267 : mOnSubscriptionEstablished(*mReadClient.get(), aSubscriptionId);
268 : }
269 : }
270 :
271 : CHIP_ERROR OnResubscriptionNeeded(chip::app::ReadClient * apReadClient, CHIP_ERROR aTerminationCause) override
272 : {
273 : ReturnErrorOnFailure(app::ReadClient::Callback::OnResubscriptionNeeded(apReadClient, aTerminationCause));
274 :
275 : if (mOnResubscriptionAttempt)
276 : {
277 : mOnResubscriptionAttempt(*mReadClient.get(), aTerminationCause, apReadClient->ComputeTimeTillNextSubscription());
278 : }
279 :
280 : return CHIP_NO_ERROR;
281 : }
282 :
283 : OnSuccessCallbackType mOnSuccess;
284 : OnErrorCallbackType mOnError;
285 : OnDoneCallbackType mOnDone;
286 : OnSubscriptionEstablishedCallbackType mOnSubscriptionEstablished;
287 : OnResubscriptionAttemptCallbackType mOnResubscriptionAttempt;
288 : Platform::UniquePtr<app::ReadClient> mReadClient;
289 : // For reads, we ensure that we make only one data/error callback to our consumer.
290 : bool mCalledCallback = false;
291 : };
292 :
293 : } // namespace Controller
294 : } // namespace chip
295 : #endif // CHIP_CONFIG_ENABLE_READ_CLIENT
|