blob: 66b4b8e293259e74830c6163004c38cff4bce0ac [file] [log] [blame]
/*
*
* 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/InteractionModelEngine.h>
#include <app/WriteClient.h>
#include <controller/CommandSenderAllocator.h>
#include <controller/TypedCommandCallback.h>
#include <lib/core/Optional.h>
namespace chip {
namespace Controller {
/*
* 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<void(const app::ConcreteAttributePath &)>;
//
// 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<void(const app::ConcreteAttributePath * path, CHIP_ERROR err)>;
using OnDoneCallbackType = std::function<void(app::WriteClient *)>;
WriteCallback(OnSuccessCallbackType aOnSuccess, OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone) :
mOnSuccess(aOnSuccess), mOnError(aOnError), mOnDone(aOnDone)
{}
void OnResponse(const app::WriteClient * apWriteClient, const app::ConcreteAttributePath & aPath, app::StatusIB status) override
{
if (status.IsSuccess())
{
mOnSuccess(aPath);
}
else
{
mOnError(&aPath, status.ToChipError());
}
}
void OnError(const app::WriteClient * apWriteClient, CHIP_ERROR aError) override { mOnError(nullptr, aError); }
void OnDone(app::WriteClient * apWriteClient) override
{
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;
};
/**
* 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 <typename AttrType>
CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId endpointId, ClusterId clusterId,
AttributeId attributeId, const AttrType & requestData, WriteCallback::OnSuccessCallbackType onSuccessCb,
WriteCallback::OnErrorCallbackType onErrorCb, const Optional<uint16_t> & aTimedWriteTimeoutMs,
WriteCallback::OnDoneCallbackType onDoneCb = nullptr)
{
auto callback = Platform::MakeUnique<WriteCallback>(onSuccessCb, onErrorCb, onDoneCb);
auto client = Platform::MakeUnique<app::WriteClient>(app::InteractionModelEngine::GetInstance()->GetExchangeManager(),
callback.get(), aTimedWriteTimeoutMs);
VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);
VerifyOrReturnError(client != nullptr, CHIP_ERROR_NO_MEMORY);
if (sessionHandle->IsGroupSession())
{
ReturnErrorOnFailure(
client->EncodeAttributeWritePayload(chip::app::AttributePathParams(clusterId, attributeId), requestData));
}
else
{
ReturnErrorOnFailure(
client->EncodeAttributeWritePayload(chip::app::AttributePathParams(endpointId, clusterId, attributeId), requestData));
}
ReturnErrorOnFailure(client->SendWriteRequest(sessionHandle));
// At this point the handle will ensure our callback's OnDone is always
// called.
client.release();
callback.release();
return CHIP_NO_ERROR;
}
template <typename AttributeInfo>
CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId endpointId,
const typename AttributeInfo::Type & requestData, WriteCallback::OnSuccessCallbackType onSuccessCb,
WriteCallback::OnErrorCallbackType onErrorCb, const Optional<uint16_t> & aTimedWriteTimeoutMs,
WriteCallback::OnDoneCallbackType onDoneCb = nullptr)
{
return WriteAttribute(sessionHandle, endpointId, AttributeInfo::GetClusterId(), AttributeInfo::GetAttributeId(), requestData,
onSuccessCb, onErrorCb, aTimedWriteTimeoutMs, onDoneCb);
}
template <typename AttributeInfo>
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)
{
return WriteAttribute<AttributeInfo>(sessionHandle, endpointId, requestData, onSuccessCb, onErrorCb, onDoneCb,
MakeOptional(aTimedWriteTimeoutMs), onDoneCb);
}
template <typename AttributeInfo, typename std::enable_if_t<!AttributeInfo::MustUseTimedWrite(), int> = 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)
{
return WriteAttribute<AttributeInfo>(sessionHandle, endpointId, requestData, onSuccessCb, onErrorCb, NullOptional, onDoneCb);
}
} // namespace Controller
} // namespace chip