/* * * Copyright (c) 2023 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 "core/BaseCluster.h" #include "core/Endpoint.h" #include "lib/support/logging/CHIPLogging.h" namespace matter { namespace casting { namespace core { template using ReadResponseSuccessCallbackFn = std::function before, typename TypeInfo::DecodableArgType after)>; using ReadResponseFailureCallbackFn = std::function; using WriteResponseSuccessCallbackFn = std::function; using WriteResponseFailureCallbackFn = std::function; template struct ReadAttributeContext; template struct WriteAttributeContext; template struct SubscribeAttributeContext; template class Attribute { private: bool hasValue = false; typename TypeInfo::DecodableType value; protected: memory::Weak GetEndpoint() const { return mEndpoint.lock(); } memory::Weak mEndpoint; public: Attribute(memory::Weak endpoint) { this->mEndpoint = endpoint; } ~Attribute() {} Attribute() = delete; Attribute(Attribute & other) = delete; void operator=(const Attribute &) = delete; chip::Optional GetValue() { return hasValue ? chip::MakeOptional(value) : chip::NullOptional; } /** * @brief Reads the value of the Attribute that belongs to the associated Endpoint and corresponding Cluster * @param context current context passed back in successCb/FailureCb * @param successCb Called when the Attribute is read successfully, with the value of the attribute after reading, as well as * before (if the Attribute had been previously read) * @param failureCb Called when there is a failure in reading the Attribute */ void Read(void * context, ReadResponseSuccessCallbackFn successCb, ReadResponseFailureCallbackFn failureCb) { memory::Strong endpoint = this->GetEndpoint().lock(); if (endpoint) { ReadAttributeContext * attributeContext = new ReadAttributeContext(this, endpoint, context, successCb, failureCb); endpoint->GetCastingPlayer()->FindOrEstablishSession( attributeContext, // FindOrEstablishSession success handler [](void * _context, chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle) { ReadAttributeContext * _attributeContext = static_cast *>(_context); ChipLogProgress(AppServer, "::Read() Found or established session"); // Read attribute MediaClusterBase mediaClusterBase(exchangeMgr, sessionHandle, _attributeContext->mEndpoint->GetId()); CHIP_ERROR err = mediaClusterBase.template ReadAttribute( _attributeContext, // Read success handler [](void * __context, typename TypeInfo::DecodableArgType response) { ReadAttributeContext * __attributeContext = static_cast *>(__context); ChipLogProgress(AppServer, "::Read() success"); Attribute * __attr = static_cast *>(__attributeContext->mAttribute); if (__attr->hasValue) { typename TypeInfo::DecodableType prevValue = __attr->value; __attr->value = response; __attributeContext->mSuccessCb(__attributeContext->mClientContext, chip::MakeOptional(prevValue), __attr->value); } else { __attr->hasValue = true; __attr->value = response; __attributeContext->mSuccessCb(__attributeContext->mClientContext, chip::NullOptional, __attr->value); } delete __attributeContext; }, // Read failure handler [](void * __context, CHIP_ERROR error) { ReadAttributeContext * __attributeContext = static_cast *>(__context); ChipLogError(AppServer, "::Read() failure response on EndpointId: %d with error: " "%" CHIP_ERROR_FORMAT, __attributeContext->mEndpoint->GetId(), error.Format()); __attributeContext->mFailureCb(__attributeContext->mClientContext, error); delete __attributeContext; }); // error in reading the attribute if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "::Read() failure in reading attribute on EndpointId: %d with error: " "%" CHIP_ERROR_FORMAT, _attributeContext->mEndpoint->GetId(), err.Format()); _attributeContext->mFailureCb(_attributeContext->mClientContext, err); delete _attributeContext; } }, // FindOrEstablishSession failure handler [](void * _context, const chip::ScopedNodeId & peerId, CHIP_ERROR error) { ReadAttributeContext * _attributeContext = static_cast *>(_context); ChipLogError(AppServer, "::Read() failure in retrieving session info for peerId.nodeId: " "0x" ChipLogFormatX64 ", peer.fabricIndex: %d with error: %" CHIP_ERROR_FORMAT, ChipLogValueX64(peerId.GetNodeId()), peerId.GetFabricIndex(), error.Format()); _attributeContext->mFailureCb(_attributeContext->mClientContext, error); delete _attributeContext; }); } else { ChipLogError(AppServer, "::Read() failure in retrieving Endpoint"); failureCb(context, CHIP_ERROR_INCORRECT_STATE); } } /** * @brief Writes the value of the Attribute to an associated Endpoint and corresponding Cluster * * @param requestData value of the Attribute to be written * @param context current context passed back in successCb/FailureCb * @param successCb Called when the Attribute is written successfully * @param failureCb Called when there is a failure in writing the Attribute * @param aTimedWriteTimeoutMs write timeout */ void Write(const typename TypeInfo::Type & requestData, void * context, WriteResponseSuccessCallbackFn successCb, WriteResponseFailureCallbackFn failureCb, const chip::Optional & aTimedWriteTimeoutMs) { memory::Strong endpoint = this->GetEndpoint().lock(); if (endpoint) { WriteAttributeContext * attributeContext = new WriteAttributeContext( this, endpoint, requestData, context, successCb, failureCb, aTimedWriteTimeoutMs); endpoint->GetCastingPlayer()->FindOrEstablishSession( attributeContext, // FindOrEstablishSession success handler [](void * _context, chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle) { WriteAttributeContext * _attributeContext = static_cast *>(_context); ChipLogProgress(AppServer, "::Write() Found or established session"); // Write attribute MediaClusterBase mediaClusterBase(exchangeMgr, sessionHandle, _attributeContext->mEndpoint->GetId()); CHIP_ERROR err = mediaClusterBase.template WriteAttribute( _attributeContext->mRequestData, _attributeContext, // Write success handler [](void * __context) { WriteAttributeContext * __attributeContext = static_cast *>(__context); ChipLogProgress(AppServer, "::Write() success"); __attributeContext->mSuccessCb(__attributeContext->mClientContext); delete __attributeContext; }, // Write failure handler [](void * __context, CHIP_ERROR error) { WriteAttributeContext * __attributeContext = static_cast *>(__context); ChipLogError(AppServer, "::Write() failure response on EndpointId: %d with error: " "%" CHIP_ERROR_FORMAT, __attributeContext->mEndpoint->GetId(), error.Format()); __attributeContext->mFailureCb(__attributeContext->mClientContext, error); delete __attributeContext; }, _attributeContext->mTimedWriteTimeoutMs); // error in writing to the attribute if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "::Write() failure in reading attribute on EndpointId: %d with error: " "%" CHIP_ERROR_FORMAT, _attributeContext->mEndpoint->GetId(), err.Format()); _attributeContext->mFailureCb(_attributeContext->mClientContext, err); delete _attributeContext; } }, // FindOrEstablishSession failure handler [](void * _context, const chip::ScopedNodeId & peerId, CHIP_ERROR error) { WriteAttributeContext * _attributeContext = static_cast *>(_context); ChipLogError(AppServer, "::Write() failure in retrieving session info for peerId.nodeId: " "0x" ChipLogFormatX64 ", peer.fabricIndex: %d with error: %" CHIP_ERROR_FORMAT, ChipLogValueX64(peerId.GetNodeId()), peerId.GetFabricIndex(), error.Format()); _attributeContext->mFailureCb(_attributeContext->mClientContext, error); delete _attributeContext; }); } else { ChipLogError(AppServer, "::Write() failure in retrieving Endpoint"); failureCb(context, CHIP_ERROR_INCORRECT_STATE); } } /** * @brief Subscribes to the value of the Attribute that belongs to the associated Endpoint and corresponding Cluster * * @param context current context passed back in successCb/FailureCb * @param successCb Called when the Attribute is read successfully, with the value of the attribute after reading, as well as * before (if the Attribute had been previously read) * @param failureCb Called when there is a failure in reading the Attribute * @param minIntervalFloorSeconds the requested minimum interval boundary floor in seconds for attribute udpates * @param maxIntervalCeilingSeconds the requested maximum interval boundary ceiling in seconds for attribute udpates */ void Subscribe(void * context, ReadResponseSuccessCallbackFn successCb, ReadResponseFailureCallbackFn failureCb, uint16_t minIntervalFloorSeconds, uint16_t maxIntervalCeilingSeconds) { memory::Strong endpoint = this->GetEndpoint().lock(); if (endpoint) { SubscribeAttributeContext * attributeContext = new SubscribeAttributeContext( this, endpoint, context, successCb, failureCb, minIntervalFloorSeconds, maxIntervalCeilingSeconds); endpoint->GetCastingPlayer()->FindOrEstablishSession( attributeContext, // FindOrEstablishSession success handler [](void * _context, chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle) { SubscribeAttributeContext * _attributeContext = static_cast *>(_context); ChipLogProgress(AppServer, "::Subscribe() Found or established session"); // Subscribe to attribute MediaClusterBase mediaClusterBase(exchangeMgr, sessionHandle, _attributeContext->mEndpoint->GetId()); CHIP_ERROR err = mediaClusterBase.template SubscribeAttribute( _attributeContext, // Subscription success handler [](void * __context, typename TypeInfo::DecodableArgType response) { SubscribeAttributeContext * __attributeContext = static_cast *>(__context); ChipLogProgress(AppServer, "::Subscribe() success"); Attribute * __attr = static_cast *>(__attributeContext->mAttribute); if (__attr->hasValue) { typename TypeInfo::DecodableType prevValue = __attr->value; __attr->value = response; __attributeContext->mSuccessCb(__attributeContext->mClientContext, chip::MakeOptional(prevValue), __attr->value); } else { __attr->hasValue = true; __attr->value = response; __attributeContext->mSuccessCb(__attributeContext->mClientContext, chip::NullOptional, __attr->value); } delete __attributeContext; }, // Subscription failure handler [](void * __context, CHIP_ERROR error) { SubscribeAttributeContext * __attributeContext = static_cast *>(__context); ChipLogError(AppServer, "::Subscribe() failure response on EndpointId: %d with error: " "%" CHIP_ERROR_FORMAT, __attributeContext->mEndpoint->GetId(), error.Format()); __attributeContext->mFailureCb(__attributeContext->mClientContext, error); delete __attributeContext; }, _attributeContext->mMinIntervalFloorSeconds, _attributeContext->mMaxIntervalCeilingSeconds, nullptr /* SubscriptionEstablishedCallback */, nullptr /* ResubscriptionAttemptCallback */, true /* aIsFabricFiltered */, true /* aKeepPreviousSubscriptions */); // error in subscribing to the attribute if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "::Subscribe() failure in reading attribute on EndpointId: %d with error: " "%" CHIP_ERROR_FORMAT, _attributeContext->mEndpoint->GetId(), err.Format()); _attributeContext->mFailureCb(_attributeContext->mClientContext, err); delete _attributeContext; } }, // FindOrEstablishSession failure handler [](void * _context, const chip::ScopedNodeId & peerId, CHIP_ERROR error) { SubscribeAttributeContext * _attributeContext = static_cast *>(_context); ChipLogError(AppServer, "::Subscribe() failure in retrieving session info for peerId.nodeId: " "0x" ChipLogFormatX64 ", peer.fabricIndex: %d with error: %" CHIP_ERROR_FORMAT, ChipLogValueX64(peerId.GetNodeId()), peerId.GetFabricIndex(), error.Format()); _attributeContext->mFailureCb(_attributeContext->mClientContext, error); delete _attributeContext; }); } else { ChipLogError(AppServer, "::Subscribe() failure in retrieving Endpoint"); failureCb(context, CHIP_ERROR_INCORRECT_STATE); } } }; /** * @brief Context object used by the Attribute class during the Read API's execution */ template struct ReadAttributeContext { ReadAttributeContext(void * attribute, memory::Strong endpoint, void * clientContext, ReadResponseSuccessCallbackFn successCb, ReadResponseFailureCallbackFn failureCb) : mEndpoint(endpoint), mClientContext(clientContext), mSuccessCb(successCb), mFailureCb(failureCb) { mAttribute = attribute; } void * mAttribute; memory::Strong mEndpoint; void * mClientContext; ReadResponseSuccessCallbackFn mSuccessCb; ReadResponseFailureCallbackFn mFailureCb; }; /** * @brief Context object used by the Attribute class during the Write API's execution */ template struct WriteAttributeContext { WriteAttributeContext(memory::Strong endpoint, const TypeInfoType & requestData, void * clientContext, WriteResponseSuccessCallbackFn successCb, WriteResponseFailureCallbackFn failureCb, const chip::Optional & timedWriteTimeoutMs) : mEndpoint(endpoint), mClientContext(clientContext), mSuccessCb(successCb), mFailureCb(failureCb) { mRequestData = requestData; mTimedWriteTimeoutMs = timedWriteTimeoutMs; } memory::Strong mEndpoint; TypeInfoType mRequestData; void * mClientContext; WriteResponseSuccessCallbackFn mSuccessCb; WriteResponseFailureCallbackFn mFailureCb; chip::Optional & mTimedWriteTimeoutMs; }; /** * @brief Context object used by the Attribute class during the Subscribe API's execution */ template struct SubscribeAttributeContext { SubscribeAttributeContext(void * attribute, memory::Strong endpoint, void * clientContext, ReadResponseSuccessCallbackFn successCb, ReadResponseFailureCallbackFn failureCb, uint16_t minIntervalFloorSeconds, uint16_t maxIntervalCeilingSeconds) : mEndpoint(endpoint), mClientContext(clientContext), mSuccessCb(successCb), mFailureCb(failureCb) { mAttribute = attribute; mMinIntervalFloorSeconds = minIntervalFloorSeconds; mMaxIntervalCeilingSeconds = maxIntervalCeilingSeconds; } void * mAttribute; memory::Strong mEndpoint; void * mClientContext; ReadResponseSuccessCallbackFn mSuccessCb; ReadResponseFailureCallbackFn mFailureCb; uint16_t mMinIntervalFloorSeconds; uint16_t mMaxIntervalCeilingSeconds; }; }; // namespace core }; // namespace casting }; // namespace matter