|  | /* | 
|  | * | 
|  | *    Copyright (c) 2021 Project CHIP Authors | 
|  | *    All rights reserved. | 
|  | * | 
|  | *    Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | *    you may not use this file except in compliance with the License. | 
|  | *    You may obtain a copy of the License at | 
|  | * | 
|  | *        http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | *    Unless required by applicable law or agreed to in writing, software | 
|  | *    distributed under the License is distributed on an "AS IS" BASIS, | 
|  | *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | *    See the License for the specific language governing permissions and | 
|  | *    limitations under the License. | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include <app/AttributePathParams.h> | 
|  | #include <app/InteractionModelEngine.h> | 
|  | #include <app/ReadPrepareParams.h> | 
|  | #include <controller/TypedReadCallback.h> | 
|  |  | 
|  | namespace chip { | 
|  | namespace Controller { | 
|  | namespace detail { | 
|  |  | 
|  | using SubscriptionOnDoneCallback = std::function<void(void)>; | 
|  |  | 
|  | template <typename DecodableAttributeType> | 
|  | struct ReportAttributeParams : public app::ReadPrepareParams | 
|  | { | 
|  | ReportAttributeParams(const SessionHandle & sessionHandle) : app::ReadPrepareParams(sessionHandle) | 
|  | { | 
|  | mKeepSubscriptions = false; | 
|  | } | 
|  | typename TypedReadAttributeCallback<DecodableAttributeType>::OnSuccessCallbackType mOnReportCb; | 
|  | typename TypedReadAttributeCallback<DecodableAttributeType>::OnErrorCallbackType mOnErrorCb; | 
|  | typename TypedReadAttributeCallback<DecodableAttributeType>::OnSubscriptionEstablishedCallbackType | 
|  | mOnSubscriptionEstablishedCb = nullptr; | 
|  | typename TypedReadAttributeCallback<DecodableAttributeType>::OnResubscriptionAttemptCallbackType mOnResubscriptionAttemptCb = | 
|  | nullptr; | 
|  | SubscriptionOnDoneCallback mOnDoneCb         = nullptr; | 
|  | app::ReadClient::InteractionType mReportType = app::ReadClient::InteractionType::Read; | 
|  | }; | 
|  |  | 
|  | template <typename DecodableAttributeType> | 
|  | CHIP_ERROR ReportAttribute(Messaging::ExchangeManager * exchangeMgr, EndpointId endpointId, ClusterId clusterId, | 
|  | AttributeId attributeId, ReportAttributeParams<DecodableAttributeType> && readParams, | 
|  | const Optional<DataVersion> & aDataVersion = NullOptional) | 
|  | { | 
|  | app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); | 
|  | CHIP_ERROR err                       = CHIP_NO_ERROR; | 
|  |  | 
|  | auto readPaths = Platform::MakeUnique<app::AttributePathParams>(endpointId, clusterId, attributeId); | 
|  | VerifyOrReturnError(readPaths != nullptr, CHIP_ERROR_NO_MEMORY); | 
|  | readParams.mpAttributePathParamsList    = readPaths.get(); | 
|  | readParams.mAttributePathParamsListSize = 1; | 
|  | chip::Platform::UniquePtr<chip::app::DataVersionFilter> dataVersionFilters; | 
|  | if (aDataVersion.HasValue()) | 
|  | { | 
|  | dataVersionFilters = Platform::MakeUnique<app::DataVersionFilter>(endpointId, clusterId, aDataVersion.Value()); | 
|  | VerifyOrReturnError(dataVersionFilters != nullptr, CHIP_ERROR_NO_MEMORY); | 
|  | readParams.mpDataVersionFilterList    = dataVersionFilters.get(); | 
|  | readParams.mDataVersionFilterListSize = 1; | 
|  | } | 
|  | auto onDoneCb = readParams.mOnDoneCb; | 
|  | auto onDone   = [onDoneCb](TypedReadAttributeCallback<DecodableAttributeType> * callback) { | 
|  | if (onDoneCb) | 
|  | { | 
|  | onDoneCb(); | 
|  | } | 
|  | chip::Platform::Delete(callback); | 
|  | }; | 
|  |  | 
|  | auto callback = chip::Platform::MakeUnique<TypedReadAttributeCallback<DecodableAttributeType>>( | 
|  | clusterId, attributeId, readParams.mOnReportCb, readParams.mOnErrorCb, onDone, readParams.mOnSubscriptionEstablishedCb, | 
|  | readParams.mOnResubscriptionAttemptCb); | 
|  | VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY); | 
|  |  | 
|  | auto readClient = | 
|  | chip::Platform::MakeUnique<app::ReadClient>(engine, exchangeMgr, callback->GetBufferedCallback(), readParams.mReportType); | 
|  | VerifyOrReturnError(readClient != nullptr, CHIP_ERROR_NO_MEMORY); | 
|  |  | 
|  | if (readClient->IsSubscriptionType()) | 
|  | { | 
|  | readPaths.release(); | 
|  | dataVersionFilters.release(); | 
|  |  | 
|  | err = readClient->SendAutoResubscribeRequest(std::move(readParams)); | 
|  | ReturnErrorOnFailure(err); | 
|  | } | 
|  | else | 
|  | { | 
|  | err = readClient->SendRequest(readParams); | 
|  | ReturnErrorOnFailure(err); | 
|  | } | 
|  |  | 
|  | // | 
|  | // At this point, we'll get a callback through the OnDone callback above regardless of success or failure | 
|  | // of the read operation to permit us to free up the callback object. So, release ownership of the callback | 
|  | // object now to prevent it from being reclaimed at the end of this scoped block. | 
|  | // | 
|  | callback->AdoptReadClient(std::move(readClient)); | 
|  | callback.release(); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | } // namespace detail | 
|  |  | 
|  | /** | 
|  | * To avoid instantiating all the complicated read code on a per-attribute | 
|  | * basis, we have a helper that's just templated on the type. | 
|  | */ | 
|  | template <typename DecodableAttributeType> | 
|  | CHIP_ERROR ReadAttribute(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, | 
|  | ClusterId clusterId, AttributeId attributeId, | 
|  | typename TypedReadAttributeCallback<DecodableAttributeType>::OnSuccessCallbackType onSuccessCb, | 
|  | typename TypedReadAttributeCallback<DecodableAttributeType>::OnErrorCallbackType onErrorCb, | 
|  | bool fabricFiltered = true) | 
|  | { | 
|  | detail::ReportAttributeParams<DecodableAttributeType> params(sessionHandle); | 
|  | params.mOnReportCb       = onSuccessCb; | 
|  | params.mOnErrorCb        = onErrorCb; | 
|  | params.mIsFabricFiltered = fabricFiltered; | 
|  | return detail::ReportAttribute(exchangeMgr, endpointId, clusterId, attributeId, std::move(params), NullOptional); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * A typed read attribute function that takes as input a template parameter that encapsulates the type information | 
|  | * for a given attribute as well as callbacks for success and failure and either returns a decoded cluster-object representation | 
|  | * of the requested attribute through the provided success callback or calls the provided failure callback. | 
|  | * | 
|  | * The AttributeTypeInfo is generally expected to be a ClusterName::Attributes::AttributeName::TypeInfo struct, but any | 
|  | * object that contains type information exposed through a 'DecodableType' type declaration as well as GetClusterId() and | 
|  | * GetAttributeId() methods is expected to work. | 
|  | */ | 
|  | template <typename AttributeTypeInfo> | 
|  | CHIP_ERROR | 
|  | ReadAttribute(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, | 
|  | typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnSuccessCallbackType onSuccessCb, | 
|  | typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnErrorCallbackType onErrorCb, | 
|  | bool fabricFiltered = true) | 
|  | { | 
|  | return ReadAttribute<typename AttributeTypeInfo::DecodableType>( | 
|  | exchangeMgr, sessionHandle, endpointId, AttributeTypeInfo::GetClusterId(), AttributeTypeInfo::GetAttributeId(), onSuccessCb, | 
|  | onErrorCb, fabricFiltered); | 
|  | } | 
|  |  | 
|  | // Helper for SubscribeAttribute to reduce the amount of code generated. | 
|  | template <typename DecodableAttributeType> | 
|  | CHIP_ERROR SubscribeAttribute( | 
|  | Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, ClusterId clusterId, | 
|  | AttributeId attributeId, typename TypedReadAttributeCallback<DecodableAttributeType>::OnSuccessCallbackType onReportCb, | 
|  | typename TypedReadAttributeCallback<DecodableAttributeType>::OnErrorCallbackType onErrorCb, uint16_t minIntervalFloorSeconds, | 
|  | uint16_t maxIntervalCeilingSeconds, | 
|  | typename TypedReadAttributeCallback<DecodableAttributeType>::OnSubscriptionEstablishedCallbackType onSubscriptionEstablishedCb = | 
|  | nullptr, | 
|  | typename TypedReadAttributeCallback<DecodableAttributeType>::OnResubscriptionAttemptCallbackType onResubscriptionAttemptCb = | 
|  | nullptr, | 
|  | bool fabricFiltered = true, bool keepPreviousSubscriptions = false, const Optional<DataVersion> & aDataVersion = NullOptional, | 
|  | typename detail::SubscriptionOnDoneCallback onDoneCb = nullptr) | 
|  | { | 
|  | detail::ReportAttributeParams<DecodableAttributeType> params(sessionHandle); | 
|  | params.mOnReportCb                  = onReportCb; | 
|  | params.mOnErrorCb                   = onErrorCb; | 
|  | params.mOnSubscriptionEstablishedCb = onSubscriptionEstablishedCb; | 
|  | params.mOnResubscriptionAttemptCb   = onResubscriptionAttemptCb; | 
|  | params.mOnDoneCb                    = onDoneCb; | 
|  | params.mMinIntervalFloorSeconds     = minIntervalFloorSeconds; | 
|  | params.mMaxIntervalCeilingSeconds   = maxIntervalCeilingSeconds; | 
|  | params.mKeepSubscriptions           = keepPreviousSubscriptions; | 
|  | params.mReportType                  = app::ReadClient::InteractionType::Subscribe; | 
|  | params.mIsFabricFiltered            = fabricFiltered; | 
|  | return detail::ReportAttribute(exchangeMgr, endpointId, clusterId, attributeId, std::move(params), aDataVersion); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * A typed way to subscribe to the value of a single attribute.  See | 
|  | * documentation for ReadAttribute above for details on how AttributeTypeInfo | 
|  | * works. | 
|  | * | 
|  | * A const view-only reference to the underlying ReadClient is passed in through the OnSubscriptionEstablishedCallbackType | 
|  | * argument. This reference is valid until the error callback is invoked at which point, this reference is no longer valid | 
|  | * and should not be used any more. | 
|  | */ | 
|  | template <typename AttributeTypeInfo> | 
|  | CHIP_ERROR SubscribeAttribute( | 
|  | Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, | 
|  | typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnSuccessCallbackType onReportCb, | 
|  | typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnErrorCallbackType onErrorCb, | 
|  | uint16_t aMinIntervalFloorSeconds, uint16_t aMaxIntervalCeilingSeconds, | 
|  | typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnSubscriptionEstablishedCallbackType | 
|  | onSubscriptionEstablishedCb = nullptr, | 
|  | typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnResubscriptionAttemptCallbackType | 
|  | onResubscriptionAttemptCb = nullptr, | 
|  | bool fabricFiltered = true, bool keepPreviousSubscriptions = false, const Optional<DataVersion> & aDataVersion = NullOptional, | 
|  | typename detail::SubscriptionOnDoneCallback onDoneCb = nullptr) | 
|  | { | 
|  | return SubscribeAttribute<typename AttributeTypeInfo::DecodableType>( | 
|  | exchangeMgr, sessionHandle, endpointId, AttributeTypeInfo::GetClusterId(), AttributeTypeInfo::GetAttributeId(), onReportCb, | 
|  | onErrorCb, aMinIntervalFloorSeconds, aMaxIntervalCeilingSeconds, onSubscriptionEstablishedCb, onResubscriptionAttemptCb, | 
|  | fabricFiltered, keepPreviousSubscriptions, aDataVersion, onDoneCb); | 
|  | } | 
|  |  | 
|  | namespace detail { | 
|  |  | 
|  | template <typename DecodableEventType> | 
|  | struct ReportEventParams : public app::ReadPrepareParams | 
|  | { | 
|  | ReportEventParams(const SessionHandle & sessionHandle) : app::ReadPrepareParams(sessionHandle) {} | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnSuccessCallbackType mOnReportCb; | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnErrorCallbackType mOnErrorCb; | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnDoneCallbackType mOnDoneCb; | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnSubscriptionEstablishedCallbackType mOnSubscriptionEstablishedCb = | 
|  | nullptr; | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnResubscriptionAttemptCallbackType mOnResubscriptionAttemptCb = nullptr; | 
|  | app::ReadClient::InteractionType mReportType = app::ReadClient::InteractionType::Read; | 
|  | }; | 
|  |  | 
|  | template <typename DecodableEventType> | 
|  | CHIP_ERROR ReportEvent(Messaging::ExchangeManager * apExchangeMgr, EndpointId endpointId, | 
|  | ReportEventParams<DecodableEventType> && readParams, bool aIsUrgentEvent) | 
|  | { | 
|  | ClusterId clusterId                  = DecodableEventType::GetClusterId(); | 
|  | EventId eventId                      = DecodableEventType::GetEventId(); | 
|  | app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); | 
|  | CHIP_ERROR err                       = CHIP_NO_ERROR; | 
|  |  | 
|  | auto readPaths = Platform::MakeUnique<app::EventPathParams>(endpointId, clusterId, eventId, aIsUrgentEvent); | 
|  | VerifyOrReturnError(readPaths != nullptr, CHIP_ERROR_NO_MEMORY); | 
|  |  | 
|  | readParams.mpEventPathParamsList = readPaths.get(); | 
|  |  | 
|  | readParams.mEventPathParamsListSize = 1; | 
|  |  | 
|  | auto callback = chip::Platform::MakeUnique<TypedReadEventCallback<DecodableEventType>>( | 
|  | readParams.mOnReportCb, readParams.mOnErrorCb, readParams.mOnDoneCb, readParams.mOnSubscriptionEstablishedCb, | 
|  | readParams.mOnResubscriptionAttemptCb); | 
|  |  | 
|  | VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY); | 
|  |  | 
|  | auto readClient = chip::Platform::MakeUnique<app::ReadClient>(engine, apExchangeMgr, *callback.get(), readParams.mReportType); | 
|  | VerifyOrReturnError(readClient != nullptr, CHIP_ERROR_NO_MEMORY); | 
|  |  | 
|  | if (readClient->IsSubscriptionType()) | 
|  | { | 
|  | readPaths.release(); | 
|  | err = readClient->SendAutoResubscribeRequest(std::move(readParams)); | 
|  | ReturnErrorOnFailure(err); | 
|  | } | 
|  | else | 
|  | { | 
|  | err = readClient->SendRequest(readParams); | 
|  | ReturnErrorOnFailure(err); | 
|  | } | 
|  |  | 
|  | // | 
|  | // At this point, we'll get a callback through the OnDone callback above regardless of success or failure | 
|  | // of the read operation to permit us to free up the callback object. So, release ownership of the callback | 
|  | // object now to prevent it from being reclaimed at the end of this scoped block. | 
|  | // | 
|  | callback->AdoptReadClient(std::move(readClient)); | 
|  | callback.release(); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | } // namespace detail | 
|  |  | 
|  | /* | 
|  | * A typed read event function that takes as input a template parameter that encapsulates the type information | 
|  | * for a given attribute as well as callbacks for success and failure and either returns a decoded cluster-object representation | 
|  | * of the requested attribute through the provided success callback or calls the provided failure callback. | 
|  | * | 
|  | * The DecodableEventType is generally expected to be a ClusterName::Events::EventName::DecodableEventType struct, but any | 
|  | * object that contains type information exposed through a 'DecodableType' type declaration as well as GetClusterId() and | 
|  | * GetEventId() methods is expected to work. | 
|  | * | 
|  | * @param[in] onSuccessCb Used to deliver event data received through the Read interactions | 
|  | * @param[in] onErrorCb failureCb will be called when an error occurs *after* a successful call to ReadEvent. | 
|  | * @param[in] onDoneCb    OnDone will be called when ReadClient has finished all work for event retrieval, it is possible that there | 
|  | * is no event. | 
|  | */ | 
|  | template <typename DecodableEventType> | 
|  | CHIP_ERROR ReadEvent(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnSuccessCallbackType onSuccessCb, | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnErrorCallbackType onErrorCb, | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnDoneCallbackType onDoneCb) | 
|  | { | 
|  | detail::ReportEventParams<DecodableEventType> params(sessionHandle); | 
|  | params.mOnReportCb = onSuccessCb; | 
|  | params.mOnErrorCb  = onErrorCb; | 
|  | params.mOnDoneCb   = onDoneCb; | 
|  | return detail::ReportEvent(exchangeMgr, endpointId, std::move(params), false /*aIsUrgentEvent*/); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * A functon that allows subscribing to one particular event.  This works | 
|  | * similarly to ReadEvent but keeps reporting events as they are emitted. | 
|  | */ | 
|  | template <typename DecodableEventType> | 
|  | CHIP_ERROR SubscribeEvent( | 
|  | Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnSuccessCallbackType onReportCb, | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnErrorCallbackType onErrorCb, uint16_t minIntervalFloorSeconds, | 
|  | uint16_t maxIntervalCeilingSeconds, | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnSubscriptionEstablishedCallbackType onSubscriptionEstablishedCb = | 
|  | nullptr, | 
|  | typename TypedReadEventCallback<DecodableEventType>::OnResubscriptionAttemptCallbackType onResubscriptionAttemptCb = nullptr, | 
|  | bool keepPreviousSubscriptions = false, bool aIsUrgentEvent = false) | 
|  | { | 
|  | detail::ReportEventParams<DecodableEventType> params(sessionHandle); | 
|  | params.mOnReportCb                  = onReportCb; | 
|  | params.mOnErrorCb                   = onErrorCb; | 
|  | params.mOnDoneCb                    = nullptr; | 
|  | params.mOnSubscriptionEstablishedCb = onSubscriptionEstablishedCb; | 
|  | params.mOnResubscriptionAttemptCb   = onResubscriptionAttemptCb; | 
|  | params.mMinIntervalFloorSeconds     = minIntervalFloorSeconds; | 
|  | params.mMaxIntervalCeilingSeconds   = maxIntervalCeilingSeconds; | 
|  | params.mKeepSubscriptions           = keepPreviousSubscriptions; | 
|  | params.mReportType                  = app::ReadClient::InteractionType::Subscribe; | 
|  | return detail::ReportEvent(exchangeMgr, endpointId, std::move(params), aIsUrgentEvent); | 
|  | } | 
|  |  | 
|  | } // namespace Controller | 
|  | } // namespace chip |