/* * * 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 #include #include #include #include #if CHIP_CONFIG_ENABLE_READ_CLIENT namespace chip { namespace Controller { namespace detail { using SubscriptionOnDoneCallback = std::function; template struct ReportAttributeParams : public app::ReadPrepareParams { ReportAttributeParams(const SessionHandle & sessionHandle) : app::ReadPrepareParams(sessionHandle) { mKeepSubscriptions = false; } typename TypedReadAttributeCallback::OnSuccessCallbackType mOnReportCb; typename TypedReadAttributeCallback::OnErrorCallbackType mOnErrorCb; typename TypedReadAttributeCallback::OnSubscriptionEstablishedCallbackType mOnSubscriptionEstablishedCb = nullptr; typename TypedReadAttributeCallback::OnResubscriptionAttemptCallbackType mOnResubscriptionAttemptCb = nullptr; SubscriptionOnDoneCallback mOnDoneCb = nullptr; app::ReadClient::InteractionType mReportType = app::ReadClient::InteractionType::Read; }; template CHIP_ERROR ReportAttribute(Messaging::ExchangeManager * exchangeMgr, EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, ReportAttributeParams && readParams, const Optional & aDataVersion = NullOptional) { app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); CHIP_ERROR err = CHIP_NO_ERROR; auto readPaths = Platform::MakeUnique(endpointId, clusterId, attributeId); VerifyOrReturnError(readPaths != nullptr, CHIP_ERROR_NO_MEMORY); readParams.mpAttributePathParamsList = readPaths.get(); readParams.mAttributePathParamsListSize = 1; chip::Platform::UniquePtr dataVersionFilters; if (aDataVersion.HasValue()) { dataVersionFilters = Platform::MakeUnique(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 * callback) { if (onDoneCb) { onDoneCb(); } chip::Platform::Delete(callback); }; auto callback = chip::Platform::MakeUnique>( clusterId, attributeId, readParams.mOnReportCb, readParams.mOnErrorCb, onDone, readParams.mOnSubscriptionEstablishedCb, readParams.mOnResubscriptionAttemptCb); VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY); auto readClient = chip::Platform::MakeUnique(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 CHIP_ERROR ReadAttribute(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, typename TypedReadAttributeCallback::OnSuccessCallbackType onSuccessCb, typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb, bool fabricFiltered = true) { detail::ReportAttributeParams 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 CHIP_ERROR ReadAttribute(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, typename TypedReadAttributeCallback::OnSuccessCallbackType onSuccessCb, typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb, bool fabricFiltered = true) { return ReadAttribute( exchangeMgr, sessionHandle, endpointId, AttributeTypeInfo::GetClusterId(), AttributeTypeInfo::GetAttributeId(), onSuccessCb, onErrorCb, fabricFiltered); } // Helper for SubscribeAttribute to reduce the amount of code generated. template CHIP_ERROR SubscribeAttribute( Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, typename TypedReadAttributeCallback::OnSuccessCallbackType onReportCb, typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb, uint16_t minIntervalFloorSeconds, uint16_t maxIntervalCeilingSeconds, typename TypedReadAttributeCallback::OnSubscriptionEstablishedCallbackType onSubscriptionEstablishedCb = nullptr, typename TypedReadAttributeCallback::OnResubscriptionAttemptCallbackType onResubscriptionAttemptCb = nullptr, bool fabricFiltered = true, bool keepPreviousSubscriptions = false, const Optional & aDataVersion = NullOptional, typename detail::SubscriptionOnDoneCallback onDoneCb = nullptr) { detail::ReportAttributeParams 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 CHIP_ERROR SubscribeAttribute( Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, typename TypedReadAttributeCallback::OnSuccessCallbackType onReportCb, typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb, uint16_t aMinIntervalFloorSeconds, uint16_t aMaxIntervalCeilingSeconds, typename TypedReadAttributeCallback::OnSubscriptionEstablishedCallbackType onSubscriptionEstablishedCb = nullptr, typename TypedReadAttributeCallback::OnResubscriptionAttemptCallbackType onResubscriptionAttemptCb = nullptr, bool fabricFiltered = true, bool keepPreviousSubscriptions = false, const Optional & aDataVersion = NullOptional, typename detail::SubscriptionOnDoneCallback onDoneCb = nullptr) { return SubscribeAttribute( exchangeMgr, sessionHandle, endpointId, AttributeTypeInfo::GetClusterId(), AttributeTypeInfo::GetAttributeId(), onReportCb, onErrorCb, aMinIntervalFloorSeconds, aMaxIntervalCeilingSeconds, onSubscriptionEstablishedCb, onResubscriptionAttemptCb, fabricFiltered, keepPreviousSubscriptions, aDataVersion, onDoneCb); } namespace detail { template struct ReportEventParams : public app::ReadPrepareParams { ReportEventParams(const SessionHandle & sessionHandle) : app::ReadPrepareParams(sessionHandle) {} typename TypedReadEventCallback::OnSuccessCallbackType mOnReportCb; typename TypedReadEventCallback::OnErrorCallbackType mOnErrorCb; typename TypedReadEventCallback::OnDoneCallbackType mOnDoneCb; typename TypedReadEventCallback::OnSubscriptionEstablishedCallbackType mOnSubscriptionEstablishedCb = nullptr; typename TypedReadEventCallback::OnResubscriptionAttemptCallbackType mOnResubscriptionAttemptCb = nullptr; app::ReadClient::InteractionType mReportType = app::ReadClient::InteractionType::Read; }; template CHIP_ERROR ReportEvent(Messaging::ExchangeManager * apExchangeMgr, EndpointId endpointId, ReportEventParams && 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(endpointId, clusterId, eventId, aIsUrgentEvent); VerifyOrReturnError(readPaths != nullptr, CHIP_ERROR_NO_MEMORY); readParams.mpEventPathParamsList = readPaths.get(); readParams.mEventPathParamsListSize = 1; auto callback = chip::Platform::MakeUnique>( readParams.mOnReportCb, readParams.mOnErrorCb, readParams.mOnDoneCb, readParams.mOnSubscriptionEstablishedCb, readParams.mOnResubscriptionAttemptCb); VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY); auto readClient = chip::Platform::MakeUnique(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 CHIP_ERROR ReadEvent(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, typename TypedReadEventCallback::OnSuccessCallbackType onSuccessCb, typename TypedReadEventCallback::OnErrorCallbackType onErrorCb, typename TypedReadEventCallback::OnDoneCallbackType onDoneCb) { detail::ReportEventParams 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 CHIP_ERROR SubscribeEvent( Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, typename TypedReadEventCallback::OnSuccessCallbackType onReportCb, typename TypedReadEventCallback::OnErrorCallbackType onErrorCb, uint16_t minIntervalFloorSeconds, uint16_t maxIntervalCeilingSeconds, typename TypedReadEventCallback::OnSubscriptionEstablishedCallbackType onSubscriptionEstablishedCb = nullptr, typename TypedReadEventCallback::OnResubscriptionAttemptCallbackType onResubscriptionAttemptCb = nullptr, bool keepPreviousSubscriptions = false, bool aIsUrgentEvent = false) { detail::ReportEventParams 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 #endif // CHIP_CONFIG_ENABLE_READ_CLIENT