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/AttributePathParams.h>
23 : #include <app/InteractionModelEngine.h>
24 : #include <app/ReadClient.h>
25 : #include <app/ReadPrepareParams.h>
26 : #include <controller/TypedReadCallback.h>
27 :
28 : #if CHIP_CONFIG_ENABLE_READ_CLIENT
29 : namespace chip {
30 : namespace Controller {
31 : namespace detail {
32 :
33 : using SubscriptionOnDoneCallback = std::function<void(void)>;
34 :
35 : template <typename DecodableAttributeType>
36 : struct ReportAttributeParams : public app::ReadPrepareParams
37 : {
38 0 : ReportAttributeParams(const SessionHandle & sessionHandle) : app::ReadPrepareParams(sessionHandle)
39 : {
40 0 : mKeepSubscriptions = false;
41 0 : }
42 : typename TypedReadAttributeCallback<DecodableAttributeType>::OnSuccessCallbackType mOnReportCb;
43 : typename TypedReadAttributeCallback<DecodableAttributeType>::OnErrorCallbackType mOnErrorCb;
44 : typename TypedReadAttributeCallback<DecodableAttributeType>::OnSubscriptionEstablishedCallbackType
45 : mOnSubscriptionEstablishedCb = nullptr;
46 : typename TypedReadAttributeCallback<DecodableAttributeType>::OnResubscriptionAttemptCallbackType mOnResubscriptionAttemptCb =
47 : nullptr;
48 : SubscriptionOnDoneCallback mOnDoneCb = nullptr;
49 : app::ReadClient::InteractionType mReportType = app::ReadClient::InteractionType::Read;
50 : };
51 :
52 : template <typename DecodableAttributeType>
53 0 : CHIP_ERROR ReportAttribute(Messaging::ExchangeManager * exchangeMgr, EndpointId endpointId, ClusterId clusterId,
54 : AttributeId attributeId, ReportAttributeParams<DecodableAttributeType> && readParams,
55 : const Optional<DataVersion> & aDataVersion = NullOptional)
56 : {
57 0 : app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
58 0 : CHIP_ERROR err = CHIP_NO_ERROR;
59 :
60 0 : auto readPaths = Platform::MakeUnique<app::AttributePathParams>(endpointId, clusterId, attributeId);
61 0 : VerifyOrReturnError(readPaths != nullptr, CHIP_ERROR_NO_MEMORY);
62 0 : readParams.mpAttributePathParamsList = readPaths.get();
63 0 : readParams.mAttributePathParamsListSize = 1;
64 0 : chip::Platform::UniquePtr<chip::app::DataVersionFilter> dataVersionFilters;
65 0 : if (aDataVersion.HasValue())
66 : {
67 0 : dataVersionFilters = Platform::MakeUnique<app::DataVersionFilter>(endpointId, clusterId, aDataVersion.Value());
68 0 : VerifyOrReturnError(dataVersionFilters != nullptr, CHIP_ERROR_NO_MEMORY);
69 0 : readParams.mpDataVersionFilterList = dataVersionFilters.get();
70 0 : readParams.mDataVersionFilterListSize = 1;
71 : }
72 0 : auto onDoneCb = readParams.mOnDoneCb;
73 0 : auto onDone = [onDoneCb](TypedReadAttributeCallback<DecodableAttributeType> * callback) {
74 0 : if (onDoneCb)
75 : {
76 0 : onDoneCb();
77 : }
78 0 : chip::Platform::Delete(callback);
79 : };
80 :
81 0 : auto callback = chip::Platform::MakeUnique<TypedReadAttributeCallback<DecodableAttributeType>>(
82 0 : clusterId, attributeId, readParams.mOnReportCb, readParams.mOnErrorCb, onDone, readParams.mOnSubscriptionEstablishedCb,
83 0 : readParams.mOnResubscriptionAttemptCb);
84 0 : VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);
85 :
86 0 : auto readClient =
87 0 : chip::Platform::MakeUnique<app::ReadClient>(engine, exchangeMgr, callback->GetBufferedCallback(), readParams.mReportType);
88 0 : VerifyOrReturnError(readClient != nullptr, CHIP_ERROR_NO_MEMORY);
89 :
90 0 : if (readClient->IsSubscriptionType())
91 : {
92 0 : readPaths.release();
93 0 : dataVersionFilters.release();
94 :
95 0 : err = readClient->SendAutoResubscribeRequest(std::move(readParams));
96 0 : ReturnErrorOnFailure(err);
97 : }
98 : else
99 : {
100 0 : err = readClient->SendRequest(readParams);
101 0 : ReturnErrorOnFailure(err);
102 : }
103 :
104 : //
105 : // At this point, we'll get a callback through the OnDone callback above regardless of success or failure
106 : // of the read operation to permit us to free up the callback object. So, release ownership of the callback
107 : // object now to prevent it from being reclaimed at the end of this scoped block.
108 : //
109 0 : callback->AdoptReadClient(std::move(readClient));
110 0 : callback.release();
111 :
112 0 : return err;
113 0 : }
114 :
115 : } // namespace detail
116 :
117 : /**
118 : * To avoid instantiating all the complicated read code on a per-attribute
119 : * basis, we have a helper that's just templated on the type.
120 : */
121 : template <typename DecodableAttributeType>
122 0 : CHIP_ERROR ReadAttribute(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId,
123 : ClusterId clusterId, AttributeId attributeId,
124 : typename TypedReadAttributeCallback<DecodableAttributeType>::OnSuccessCallbackType onSuccessCb,
125 : typename TypedReadAttributeCallback<DecodableAttributeType>::OnErrorCallbackType onErrorCb,
126 : bool fabricFiltered = true)
127 : {
128 0 : detail::ReportAttributeParams<DecodableAttributeType> params(sessionHandle);
129 0 : params.mOnReportCb = onSuccessCb;
130 0 : params.mOnErrorCb = onErrorCb;
131 0 : params.mIsFabricFiltered = fabricFiltered;
132 0 : return detail::ReportAttribute(exchangeMgr, endpointId, clusterId, attributeId, std::move(params), NullOptional);
133 0 : }
134 :
135 : /*
136 : * A typed read attribute function that takes as input a template parameter that encapsulates the type information
137 : * for a given attribute as well as callbacks for success and failure and either returns a decoded cluster-object representation
138 : * of the requested attribute through the provided success callback or calls the provided failure callback.
139 : *
140 : * The AttributeTypeInfo is generally expected to be a ClusterName::Attributes::AttributeName::TypeInfo struct, but any
141 : * object that contains type information exposed through a 'DecodableType' type declaration as well as GetClusterId() and
142 : * GetAttributeId() methods is expected to work.
143 : */
144 : template <typename AttributeTypeInfo>
145 : CHIP_ERROR
146 : ReadAttribute(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId,
147 : typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnSuccessCallbackType onSuccessCb,
148 : typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnErrorCallbackType onErrorCb,
149 : bool fabricFiltered = true)
150 : {
151 : return ReadAttribute<typename AttributeTypeInfo::DecodableType>(
152 : exchangeMgr, sessionHandle, endpointId, AttributeTypeInfo::GetClusterId(), AttributeTypeInfo::GetAttributeId(), onSuccessCb,
153 : onErrorCb, fabricFiltered);
154 : }
155 :
156 : // Helper for SubscribeAttribute to reduce the amount of code generated.
157 : template <typename DecodableAttributeType>
158 : CHIP_ERROR SubscribeAttribute(
159 : Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, ClusterId clusterId,
160 : AttributeId attributeId, typename TypedReadAttributeCallback<DecodableAttributeType>::OnSuccessCallbackType onReportCb,
161 : typename TypedReadAttributeCallback<DecodableAttributeType>::OnErrorCallbackType onErrorCb, uint16_t minIntervalFloorSeconds,
162 : uint16_t maxIntervalCeilingSeconds,
163 : typename TypedReadAttributeCallback<DecodableAttributeType>::OnSubscriptionEstablishedCallbackType onSubscriptionEstablishedCb =
164 : nullptr,
165 : typename TypedReadAttributeCallback<DecodableAttributeType>::OnResubscriptionAttemptCallbackType onResubscriptionAttemptCb =
166 : nullptr,
167 : bool fabricFiltered = true, bool keepPreviousSubscriptions = false, const Optional<DataVersion> & aDataVersion = NullOptional,
168 : typename detail::SubscriptionOnDoneCallback onDoneCb = nullptr)
169 : {
170 : detail::ReportAttributeParams<DecodableAttributeType> params(sessionHandle);
171 : params.mOnReportCb = onReportCb;
172 : params.mOnErrorCb = onErrorCb;
173 : params.mOnSubscriptionEstablishedCb = onSubscriptionEstablishedCb;
174 : params.mOnResubscriptionAttemptCb = onResubscriptionAttemptCb;
175 : params.mOnDoneCb = onDoneCb;
176 : params.mMinIntervalFloorSeconds = minIntervalFloorSeconds;
177 : params.mMaxIntervalCeilingSeconds = maxIntervalCeilingSeconds;
178 : params.mKeepSubscriptions = keepPreviousSubscriptions;
179 : params.mReportType = app::ReadClient::InteractionType::Subscribe;
180 : params.mIsFabricFiltered = fabricFiltered;
181 : return detail::ReportAttribute(exchangeMgr, endpointId, clusterId, attributeId, std::move(params), aDataVersion);
182 : }
183 :
184 : /*
185 : * A typed way to subscribe to the value of a single attribute. See
186 : * documentation for ReadAttribute above for details on how AttributeTypeInfo
187 : * works.
188 : *
189 : * A const view-only reference to the underlying ReadClient is passed in through the OnSubscriptionEstablishedCallbackType
190 : * argument. This reference is valid until the error callback is invoked at which point, this reference is no longer valid
191 : * and should not be used any more.
192 : */
193 : template <typename AttributeTypeInfo>
194 : CHIP_ERROR SubscribeAttribute(
195 : Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId,
196 : typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnSuccessCallbackType onReportCb,
197 : typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnErrorCallbackType onErrorCb,
198 : uint16_t aMinIntervalFloorSeconds, uint16_t aMaxIntervalCeilingSeconds,
199 : typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnSubscriptionEstablishedCallbackType
200 : onSubscriptionEstablishedCb = nullptr,
201 : typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnResubscriptionAttemptCallbackType
202 : onResubscriptionAttemptCb = nullptr,
203 : bool fabricFiltered = true, bool keepPreviousSubscriptions = false, const Optional<DataVersion> & aDataVersion = NullOptional,
204 : typename detail::SubscriptionOnDoneCallback onDoneCb = nullptr)
205 : {
206 : return SubscribeAttribute<typename AttributeTypeInfo::DecodableType>(
207 : exchangeMgr, sessionHandle, endpointId, AttributeTypeInfo::GetClusterId(), AttributeTypeInfo::GetAttributeId(), onReportCb,
208 : onErrorCb, aMinIntervalFloorSeconds, aMaxIntervalCeilingSeconds, onSubscriptionEstablishedCb, onResubscriptionAttemptCb,
209 : fabricFiltered, keepPreviousSubscriptions, aDataVersion, onDoneCb);
210 : }
211 :
212 : namespace detail {
213 :
214 : template <typename DecodableEventType>
215 : struct ReportEventParams : public app::ReadPrepareParams
216 : {
217 : ReportEventParams(const SessionHandle & sessionHandle) : app::ReadPrepareParams(sessionHandle) {}
218 : typename TypedReadEventCallback<DecodableEventType>::OnSuccessCallbackType mOnReportCb;
219 : typename TypedReadEventCallback<DecodableEventType>::OnErrorCallbackType mOnErrorCb;
220 : typename TypedReadEventCallback<DecodableEventType>::OnDoneCallbackType mOnDoneCb;
221 : typename TypedReadEventCallback<DecodableEventType>::OnSubscriptionEstablishedCallbackType mOnSubscriptionEstablishedCb =
222 : nullptr;
223 : typename TypedReadEventCallback<DecodableEventType>::OnResubscriptionAttemptCallbackType mOnResubscriptionAttemptCb = nullptr;
224 : app::ReadClient::InteractionType mReportType = app::ReadClient::InteractionType::Read;
225 : };
226 :
227 : template <typename DecodableEventType>
228 : CHIP_ERROR ReportEvent(Messaging::ExchangeManager * apExchangeMgr, EndpointId endpointId,
229 : ReportEventParams<DecodableEventType> && readParams, bool aIsUrgentEvent)
230 : {
231 : ClusterId clusterId = DecodableEventType::GetClusterId();
232 : EventId eventId = DecodableEventType::GetEventId();
233 : app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
234 : CHIP_ERROR err = CHIP_NO_ERROR;
235 :
236 : auto readPaths = Platform::MakeUnique<app::EventPathParams>(endpointId, clusterId, eventId, aIsUrgentEvent);
237 : VerifyOrReturnError(readPaths != nullptr, CHIP_ERROR_NO_MEMORY);
238 :
239 : readParams.mpEventPathParamsList = readPaths.get();
240 :
241 : readParams.mEventPathParamsListSize = 1;
242 :
243 : auto callback = chip::Platform::MakeUnique<TypedReadEventCallback<DecodableEventType>>(
244 : readParams.mOnReportCb, readParams.mOnErrorCb, readParams.mOnDoneCb, readParams.mOnSubscriptionEstablishedCb,
245 : readParams.mOnResubscriptionAttemptCb);
246 :
247 : VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);
248 :
249 : auto readClient = chip::Platform::MakeUnique<app::ReadClient>(engine, apExchangeMgr, *callback.get(), readParams.mReportType);
250 : VerifyOrReturnError(readClient != nullptr, CHIP_ERROR_NO_MEMORY);
251 :
252 : if (readClient->IsSubscriptionType())
253 : {
254 : readPaths.release();
255 : err = readClient->SendAutoResubscribeRequest(std::move(readParams));
256 : ReturnErrorOnFailure(err);
257 : }
258 : else
259 : {
260 : err = readClient->SendRequest(readParams);
261 : ReturnErrorOnFailure(err);
262 : }
263 :
264 : //
265 : // At this point, we'll get a callback through the OnDone callback above regardless of success or failure
266 : // of the read operation to permit us to free up the callback object. So, release ownership of the callback
267 : // object now to prevent it from being reclaimed at the end of this scoped block.
268 : //
269 : callback->AdoptReadClient(std::move(readClient));
270 : callback.release();
271 :
272 : return err;
273 : }
274 :
275 : } // namespace detail
276 :
277 : /*
278 : * A typed read event function that takes as input a template parameter that encapsulates the type information
279 : * for a given attribute as well as callbacks for success and failure and either returns a decoded cluster-object representation
280 : * of the requested attribute through the provided success callback or calls the provided failure callback.
281 : *
282 : * The DecodableEventType is generally expected to be a ClusterName::Events::EventName::DecodableEventType struct, but any
283 : * object that contains type information exposed through a 'DecodableType' type declaration as well as GetClusterId() and
284 : * GetEventId() methods is expected to work.
285 : *
286 : * @param[in] onSuccessCb Used to deliver event data received through the Read interactions
287 : * @param[in] onErrorCb failureCb will be called when an error occurs *after* a successful call to ReadEvent.
288 : * @param[in] onDoneCb OnDone will be called when ReadClient has finished all work for event retrieval, it is possible that there
289 : * is no event.
290 : */
291 : template <typename DecodableEventType>
292 : CHIP_ERROR ReadEvent(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId,
293 : typename TypedReadEventCallback<DecodableEventType>::OnSuccessCallbackType onSuccessCb,
294 : typename TypedReadEventCallback<DecodableEventType>::OnErrorCallbackType onErrorCb,
295 : typename TypedReadEventCallback<DecodableEventType>::OnDoneCallbackType onDoneCb)
296 : {
297 : detail::ReportEventParams<DecodableEventType> params(sessionHandle);
298 : params.mOnReportCb = onSuccessCb;
299 : params.mOnErrorCb = onErrorCb;
300 : params.mOnDoneCb = onDoneCb;
301 : return detail::ReportEvent(exchangeMgr, endpointId, std::move(params), false /*aIsUrgentEvent*/);
302 : }
303 :
304 : /**
305 : * A functon that allows subscribing to one particular event. This works
306 : * similarly to ReadEvent but keeps reporting events as they are emitted.
307 : */
308 : template <typename DecodableEventType>
309 : CHIP_ERROR SubscribeEvent(
310 : Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId,
311 : typename TypedReadEventCallback<DecodableEventType>::OnSuccessCallbackType onReportCb,
312 : typename TypedReadEventCallback<DecodableEventType>::OnErrorCallbackType onErrorCb, uint16_t minIntervalFloorSeconds,
313 : uint16_t maxIntervalCeilingSeconds,
314 : typename TypedReadEventCallback<DecodableEventType>::OnSubscriptionEstablishedCallbackType onSubscriptionEstablishedCb =
315 : nullptr,
316 : typename TypedReadEventCallback<DecodableEventType>::OnResubscriptionAttemptCallbackType onResubscriptionAttemptCb = nullptr,
317 : bool keepPreviousSubscriptions = false, bool aIsUrgentEvent = false)
318 : {
319 : detail::ReportEventParams<DecodableEventType> params(sessionHandle);
320 : params.mOnReportCb = onReportCb;
321 : params.mOnErrorCb = onErrorCb;
322 : params.mOnDoneCb = nullptr;
323 : params.mOnSubscriptionEstablishedCb = onSubscriptionEstablishedCb;
324 : params.mOnResubscriptionAttemptCb = onResubscriptionAttemptCb;
325 : params.mMinIntervalFloorSeconds = minIntervalFloorSeconds;
326 : params.mMaxIntervalCeilingSeconds = maxIntervalCeilingSeconds;
327 : params.mKeepSubscriptions = keepPreviousSubscriptions;
328 : params.mReportType = app::ReadClient::InteractionType::Subscribe;
329 : return detail::ReportEvent(exchangeMgr, endpointId, std::move(params), aIsUrgentEvent);
330 : }
331 :
332 : } // namespace Controller
333 : } // namespace chip
334 : #endif // CHIP_CONFIG_ENABLE_READ_CLIENT
|