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