blob: 511e46b83163f26f403b628c9267f777c413ad4f [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.
*/
/**
* @file
* This file defines read client for a CHIP Interaction Data model
*
*/
#pragma once
#include <app/AttributePathParams.h>
#include <app/ConcreteAttributePath.h>
#include <app/EventHeader.h>
#include <app/EventPathParams.h>
#include <app/InteractionModelDelegate.h>
#include <app/MessageDef/ReadRequestMessage.h>
#include <app/MessageDef/StatusResponseMessage.h>
#include <app/MessageDef/SubscribeRequestMessage.h>
#include <app/MessageDef/SubscribeResponseMessage.h>
#include <app/ReadPrepareParams.h>
#include <app/data-model/Decode.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPTLVDebug.hpp>
#include <lib/support/CodeUtils.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/logging/CHIPLogging.h>
#include <messaging/ExchangeContext.h>
#include <messaging/ExchangeMgr.h>
#include <messaging/Flags.h>
#include <protocols/Protocols.h>
#include <system/SystemPacketBuffer.h>
namespace chip {
namespace app {
class InteractionModelEngine;
/**
* @class ReadClient
*
* @brief The read client represents the initiator side of a Read Interaction, and is responsible
* for generating one Read Request for a particular set of attributes and/or events, and handling the Report Data response.
*
*/
class ReadClient : public Messaging::ExchangeDelegate
{
public:
class Callback
{
public:
virtual ~Callback() = default;
/**
* Used to signal the commencement of processing of the first attribute report received in a given exchange.
*
* This object MUST continue to exist after this call is completed. The application shall wait until it
* receives an OnDone call to destroy the object.
*
*/
virtual void OnReportBegin(const ReadClient * apReadClient) {}
/**
* Used to signal the completion of processing of the last attribute report in a given exchange.
*
* This object MUST continue to exist after this call is completed. The application shall wait until it
* receives an OnDone call to destroy the object.
*
*/
virtual void OnReportEnd(const ReadClient * apReadClient) {}
/**
* Used to deliver event data received through the Read and Subscribe interactions
*
* Only one of the apData and apStatus can be non-null.
*
* This object MUST continue to exist after this call is completed. The application shall wait until it
* receives an OnDone call to destroy the object.
*
* @param[in] apReadClient: The read client object that initiated the read or subscribe transaction.
* @param[in] aEventHeader: The event header in report response.
* @param[in] apData: A TLVReader positioned right on the payload of the event.
* @param[in] apStatus: Event-specific status, containing an InteractionModel::Status code as well as an optional
* cluster-specific status code.
*/
virtual void OnEventData(const ReadClient * apReadClient, const EventHeader & aEventHeader, TLV::TLVReader * apData,
const StatusIB * apStatus)
{}
/**
* Used to deliver attribute data received through the Read and Subscribe interactions.
*
* This callback will be called when:
* - Receiving attribute data as response of Read interactions
* - Receiving attribute data as reports of subscriptions
* - Receiving attribute data as initial reports of subscriptions
*
* This object MUST continue to exist after this call is completed. The application shall wait until it
* receives an OnDone call to destroy the object.
*
* @param[in] apReadClient The read client object that initiated the read or subscribe transaction.
* @param[in] aPath The attribute path field in report response.
* @param[in] apData The attribute data of the given path, will be a nullptr if status is not Success.
* @param[in] aStatus Attribute-specific status, containing an InteractionModel::Status code as well as an
* optional cluster-specific status code.
*/
virtual void OnAttributeData(const ReadClient * apReadClient, const ConcreteDataAttributePath & aPath,
TLV::TLVReader * apData, const StatusIB & aStatus)
{}
/**
* OnSubscriptionEstablished will be called when a subscription is established for the given subscription transaction.
*
* This object MUST continue to exist after this call is completed. The application shall wait until it
* receives an OnDone call to destroy the object.
*
* @param[in] apReadClient The read client object that initiated the read transaction.
*/
virtual void OnSubscriptionEstablished(const ReadClient * apReadClient) {}
/**
* OnError will be called when an error occurs *after* a successful call to SendRequest(). The following
* errors will be delivered through this call in the aError field:
*
* - CHIP_ERROR_TIMEOUT: A response was not received within the expected response timeout.
* - CHIP_ERROR_*TLV*: A malformed, non-compliant response was received from the server.
* - CHIP_ERROR*: All other cases.
*
* This object MUST continue to exist after this call is completed. The application shall wait until it
* receives an OnDone call to destroy the object.
*
* @param[in] apReadClient The read client object that initiated the attribute read transaction.
* @param[in] aError A system error code that conveys the overall error code.
*/
virtual void OnError(const ReadClient * apReadClient, CHIP_ERROR aError) {}
/**
* OnDone will be called when ReadClient has finished all work and is safe to destroy and free the
* allocated CommandSender object.
*
* This function will:
* - Always be called exactly *once* for a given ReadClient instance.
* - Be called even in error circumstances.
* - Only be called after a successful call to SendRequest has been
* made, when the read completes or the subscription is shut down.
*
* @param[in] apReadClient The read client object of the terminated read or subscribe interaction.
*/
virtual void OnDone(ReadClient * apReadClient) = 0;
};
enum class InteractionType : uint8_t
{
Read,
Subscribe,
};
/**
*
* Constructor.
*
* The callback passed in has to outlive this ReadClient object.
*
* This object can outlive the InteractionModelEngine passed in. However, upon shutdown of the engine,
* this object will cease to function correctly since it depends on the engine for a number of critical functions.
*
* @param[in] apImEngine A valid pointer to the IM engine.
* @param[in] apExchangeMgr A pointer to the ExchangeManager object.
* @param[in] apCallback InteractionModelDelegate set by application.
* @param[in] aInteractionType Type of interaction (read or subscribe)
*
* @retval #CHIP_ERROR_INCORRECT_STATE incorrect state if it is already initialized
* @retval #CHIP_NO_ERROR On success.
*
*/
ReadClient(InteractionModelEngine * apImEngine, Messaging::ExchangeManager * apExchangeMgr, Callback & apCallback,
InteractionType aInteractionType);
/**
* Destructor.
*
* Will abort the exchange context if a valid one still exists. It will also cancel any
* liveness timers that may be active.
*
* OnDone() will not be called.
*/
virtual ~ReadClient();
/*
* This forcibly closes the exchange context if a valid one is pointed to. Such a situation does
* not arise during normal message processing flows that all normally call Close() above. This can only
* arise due to application-initiated destruction of the object when this object is handling receiving/sending
* message payloads. Abort() should be called first before the object is destroyed.
*/
void Abort();
/**
* Send a request. There can be one request outstanding on a given ReadClient.
* If SendRequest returns success, no more SendRequest calls can happen on this ReadClient
* until the corresponding OnDone call has happened.
*
* This will send either a Read Request or a Subscribe Request depending on
* the InteractionType this read client was initialized with.
*
* @retval #others fail to send read request
* @retval #CHIP_NO_ERROR On success.
*/
CHIP_ERROR SendRequest(ReadPrepareParams & aReadPrepareParams);
CHIP_ERROR OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload);
auto GetSubscriptionId() const
{
using returnType = Optional<decltype(mSubscriptionId)>;
return mInteractionType == InteractionType::Subscribe ? returnType(mSubscriptionId) : returnType::Missing();
}
FabricIndex GetFabricIndex() const { return mFabricIndex; }
NodeId GetPeerNodeId() const { return mPeerNodeId; }
bool IsReadType() { return mInteractionType == InteractionType::Read; }
bool IsSubscriptionType() const { return mInteractionType == InteractionType::Subscribe; };
ReadClient * GetNextClient() { return mpNext; }
void SetNextClient(ReadClient * apClient) { mpNext = apClient; }
private:
friend class TestReadInteraction;
friend class InteractionModelEngine;
enum class ClientState : uint8_t
{
Idle, ///< The client has been initialized and is ready for a SendRequest
AwaitingInitialReport, ///< The client is waiting for initial report
AwaitingSubscribeResponse, ///< The client is waiting for subscribe response
SubscriptionActive, ///< The client is maintaining subscription
};
bool IsMatchingClient(uint64_t aSubscriptionId)
{
return aSubscriptionId == mSubscriptionId && mInteractionType == InteractionType::Subscribe;
}
CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
System::PacketBufferHandle && aPayload) override;
void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override;
/**
* Check if current read client is being used
*
*/
bool IsIdle() const { return mState == ClientState::Idle; }
bool IsSubscriptionIdle() const { return mState == ClientState::SubscriptionActive; }
bool IsAwaitingInitialReport() const { return mState == ClientState::AwaitingInitialReport; }
bool IsAwaitingSubscribeResponse() const { return mState == ClientState::AwaitingSubscribeResponse; }
CHIP_ERROR GenerateEventPaths(EventPathIBs::Builder & aEventPathsBuilder, EventPathParams * apEventPathParamsList,
size_t aEventPathParamsListSize);
CHIP_ERROR GenerateAttributePathList(AttributePathIBs::Builder & aAttributePathIBsBuilder,
AttributePathParams * apAttributePathParamsList, size_t aAttributePathParamsListSize);
CHIP_ERROR ProcessAttributeReportIBs(TLV::TLVReader & aAttributeDataIBsReader);
CHIP_ERROR ProcessEventReportIBs(TLV::TLVReader & aEventReportIBsReader);
void ClearExchangeContext() { mpExchangeCtx = nullptr; }
static void OnLivenessTimeoutCallback(System::Layer * apSystemLayer, void * apAppState);
CHIP_ERROR ProcessSubscribeResponse(System::PacketBufferHandle && aPayload);
CHIP_ERROR RefreshLivenessCheckTimer();
void CancelLivenessCheckTimer();
void MoveToState(const ClientState aTargetState);
CHIP_ERROR ProcessAttributePath(AttributePathIB::Parser & aAttributePath, ConcreteDataAttributePath & aClusterInfo);
CHIP_ERROR ProcessReportData(System::PacketBufferHandle && aPayload);
const char * GetStateStr() const;
// Specialized request-sending functions.
CHIP_ERROR SendReadRequest(ReadPrepareParams & aReadPrepareParams);
CHIP_ERROR SendSubscribeRequest(ReadPrepareParams & aSubscribePrepareParams);
/*
* Called internally to signal the completion of all work on this object, gracefully close the
* exchange and finally, signal to the application that it's
* safe to release this object.
*
* If aError != CHIP_NO_ERROR, it is delivered to the application through the OnError callback first.
*
*/
void Close(CHIP_ERROR aError);
Messaging::ExchangeManager * mpExchangeMgr = nullptr;
Messaging::ExchangeContext * mpExchangeCtx = nullptr;
Callback & mpCallback;
ClientState mState = ClientState::Idle;
bool mIsInitialReport = true;
bool mIsPrimingReports = true;
bool mPendingMoreChunks = false;
uint16_t mMinIntervalFloorSeconds = 0;
uint16_t mMaxIntervalCeilingSeconds = 0;
uint64_t mSubscriptionId = 0;
NodeId mPeerNodeId = kUndefinedNodeId;
FabricIndex mFabricIndex = kUndefinedFabricIndex;
InteractionType mInteractionType = InteractionType::Read;
Timestamp mEventTimestamp;
EventNumber mEventMin = 0;
ReadClient * mpNext = nullptr;
InteractionModelEngine * mpImEngine = nullptr;
};
}; // namespace app
}; // namespace chip