/* * * 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 #include #include namespace chip { namespace Controller { namespace Internal { // WriteCancelFn functions on WriteAttribute() are for internal use only. typedef std::function WriteCancelFn; } // namespace Internal /* * An adapter callback that permits applications to provide std::function callbacks for success, error and on done. * This permits a slightly more flexible programming model that allows applications to pass in lambdas and bound member functions * as they see fit instead. * */ class WriteCallback final : public app::WriteClient::Callback { public: using OnSuccessCallbackType = std::function; // // Callback to deliver any error that occurs during the write. This includes // errors global to the write as a whole (e.g timeout) as well as per-attribute // errors. // // In the latter case, path will be non-null. Otherwise, it shall be null. // using OnErrorCallbackType = std::function; using OnDoneCallbackType = std::function; WriteCallback(OnSuccessCallbackType aOnSuccess, OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone, bool aIsGroupWrite) : mOnSuccess(aOnSuccess), mOnError(aOnError), mOnDone(aOnDone), mIsGroupWrite(aIsGroupWrite), mCallback(this) {} app::WriteClient::Callback * GetChunkedCallback() { return &mCallback; } void OnResponse(const app::WriteClient * apWriteClient, const app::ConcreteDataAttributePath & aPath, app::StatusIB status) override { if (mCalledCallback) { return; } mCalledCallback = true; if (status.IsSuccess()) { mOnSuccess(aPath); } else { mOnError(&aPath, status.ToChipError()); } } void OnError(const app::WriteClient * apWriteClient, CHIP_ERROR aError) override { if (mCalledCallback) { return; } mCalledCallback = true; mOnError(nullptr, aError); } void OnDone(app::WriteClient * apWriteClient) override { if (!mIsGroupWrite && !mCalledCallback) { // This can happen if the server sends a response with an empty // WriteResponses list. Since we are not sending wildcard write // paths, that's not a valid response and we should treat it as an // error. Use the error we would have gotten if we in fact expected // a nonempty list. OnError(apWriteClient, CHIP_END_OF_TLV); } if (mOnDone != nullptr) { mOnDone(apWriteClient); } chip::Platform::Delete(apWriteClient); // Always needs to be the last call chip::Platform::Delete(this); } private: OnSuccessCallbackType mOnSuccess = nullptr; OnErrorCallbackType mOnError = nullptr; OnDoneCallbackType mOnDone = nullptr; bool mCalledCallback = false; bool mIsGroupWrite = false; app::ChunkedWriteCallback mCallback; }; /** * Functions for writing attributes. We have lots of different AttributeInfo * but a fairly small set of types that get written. So we want to keep the * template on AttributeInfo very small, and put all the work in the template * with a small number of instantiations (one per type). */ template CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, const AttrType & requestData, WriteCallback::OnSuccessCallbackType onSuccessCb, WriteCallback::OnErrorCallbackType onErrorCb, const Optional & aTimedWriteTimeoutMs, WriteCallback::OnDoneCallbackType onDoneCb = nullptr, const Optional & aDataVersion = NullOptional, Internal::WriteCancelFn * outCancelFn = nullptr) { auto callback = Platform::MakeUnique(onSuccessCb, onErrorCb, onDoneCb, sessionHandle->IsGroupSession()); VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY); auto client = Platform::MakeUnique(app::InteractionModelEngine::GetInstance()->GetExchangeManager(), callback->GetChunkedCallback(), aTimedWriteTimeoutMs); VerifyOrReturnError(client != nullptr, CHIP_ERROR_NO_MEMORY); if (sessionHandle->IsGroupSession()) { ReturnErrorOnFailure(client->EncodeAttribute(chip::app::AttributePathParams(clusterId, attributeId), requestData)); } else { ReturnErrorOnFailure( client->EncodeAttribute(chip::app::AttributePathParams(endpointId, clusterId, attributeId), requestData, aDataVersion)); } ReturnErrorOnFailure(client->SendWriteRequest(sessionHandle)); // If requested by the caller, provide a way to cancel the write interaction. if (outCancelFn != nullptr) { *outCancelFn = [rawCallback = callback.get(), rawClient = client.get()]() { chip::Platform::Delete(rawClient); chip::Platform::Delete(rawCallback); }; } // At this point the handle will ensure our callback's OnDone is always // called. client.release(); callback.release(); return CHIP_NO_ERROR; } template CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId endpointId, const typename AttributeInfo::Type & requestData, WriteCallback::OnSuccessCallbackType onSuccessCb, WriteCallback::OnErrorCallbackType onErrorCb, const Optional & aTimedWriteTimeoutMs, WriteCallback::OnDoneCallbackType onDoneCb = nullptr, const Optional & aDataVersion = NullOptional) { return WriteAttribute(sessionHandle, endpointId, AttributeInfo::GetClusterId(), AttributeInfo::GetAttributeId(), requestData, onSuccessCb, onErrorCb, aTimedWriteTimeoutMs, onDoneCb, aDataVersion); } template CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId endpointId, const typename AttributeInfo::Type & requestData, WriteCallback::OnSuccessCallbackType onSuccessCb, WriteCallback::OnErrorCallbackType onErrorCb, uint16_t aTimedWriteTimeoutMs, WriteCallback::OnDoneCallbackType onDoneCb = nullptr, const Optional & aDataVersion = NullOptional) { return WriteAttribute(sessionHandle, endpointId, requestData, onSuccessCb, onErrorCb, onDoneCb, MakeOptional(aTimedWriteTimeoutMs), onDoneCb, aDataVersion); } template = 0> CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId endpointId, const typename AttributeInfo::Type & requestData, WriteCallback::OnSuccessCallbackType onSuccessCb, WriteCallback::OnErrorCallbackType onErrorCb, WriteCallback::OnDoneCallbackType onDoneCb = nullptr, const Optional & aDataVersion = NullOptional) { return WriteAttribute(sessionHandle, endpointId, requestData, onSuccessCb, onErrorCb, NullOptional, onDoneCb, aDataVersion); } } // namespace Controller } // namespace chip