Skip to content

Commit

Permalink
Add IM/Ember Read/Writer Interface and attribute read for IM
Browse files Browse the repository at this point in the history
Summary of Changes:
-- Add initial IM read attribute implementation and ember read/write
interface so that read client can send IM read request with attribute
path,  interaction model engine receive this request and ask
reporting engine generate the corresponding interested attributes and
send it back via read hander.

-- Add unit and cirque integration test
  • Loading branch information
yunhanw-google committed Apr 22, 2021
1 parent 0e9ec10 commit 58ba3fb
Show file tree
Hide file tree
Showing 15 changed files with 482 additions and 50 deletions.
10 changes: 1 addition & 9 deletions src/app/AttributePathParams.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,7 @@ struct AttributePathParams
mNodeId(aNodeId),
mEndpointId(aEndpointId), mClusterId(aClusterId), mFieldId(aFieldId), mListIndex(aListIndex), mFlags(aFlags)
{}
AttributePathParams(const AttributePathParams & aAttributePathParams)
{
mNodeId = aAttributePathParams.mNodeId;
mEndpointId = aAttributePathParams.mEndpointId;
mClusterId = aAttributePathParams.mClusterId;
mFieldId = aAttributePathParams.mFieldId;
mListIndex = aAttributePathParams.mListIndex;
mFlags = aAttributePathParams.mFlags;
}
AttributePathParams(){};
bool IsSamePath(const AttributePathParams & other) const
{
if (other.mNodeId != mNodeId || other.mEndpointId != mEndpointId || other.mClusterId != mClusterId)
Expand Down
1 change: 1 addition & 0 deletions src/app/ClusterInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct ClusterInfo
ClusterInfo(const AttributePathParams & aAttributePathParams, bool aDirty) :
mAttributePathParams(aAttributePathParams), mDirty(aDirty)
{}
ClusterInfo() {}
bool IsDirty() { return mDirty; }
void SetDirty() { mDirty = true; }
void ClearDirty() { mDirty = false; }
Expand Down
86 changes: 86 additions & 0 deletions src/app/InteractionModelEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,58 @@ void InteractionModelEngine::OnResponseTimeout(Messaging::ExchangeContext * ec)
ChipLogProgress(DataManagement, "Time out! failed to receive echo response from Exchange: %d", ec->GetExchangeId());
}

// Release clusterInfo list for the read handler to pool and shrink the rear of clusterInfo List forward if any
void InteractionModelEngine::ReleaseClusterInfoListToPool(ReadHandler * const apReadHandler)
{
ClusterInfo * const clusterInfoList = apReadHandler->GetClusterInfoList();
long numClusterInfos = apReadHandler->GetNumClusterInfos();
long numClusterInfosToBeAffected;

if (numClusterInfos == 0)
{
ChipLogDetail(DataManagement, "No cluster instances allocated");
return;
}

apReadHandler->ClearClusterInfo();

// make sure everything is still sane
ChipLogIfFalse(clusterInfoList >= mClusterInfoPool);
ChipLogIfFalse(numClusterInfos <= mNumClusterInfos);

// mClusterInfoPool + mNumClusterInfos is a pointer which points to the last+1byte of this array
// clusterInfoList is a pointer to the first cluster instance to be released
// the result of subtraction is the number of cluster instances from clusterInfoList to the end of this array
numClusterInfosToBeAffected = &mClusterInfoPool[mNumClusterInfos] - clusterInfoList;

// Shrink the clusterInfosInPool by the number of cluster instances.
mNumClusterInfos -= numClusterInfos;

ChipLogDetail(DataManagement, "numClusterInfos is %d, and numClusterInfosToBeAffected is %l", numClusterInfos,
numClusterInfosToBeAffected);

if (numClusterInfos == numClusterInfosToBeAffected)
{
ChipLogDetail(DataManagement, "Releasing the last block of cluster instances");
return;
}

ChipLogDetail(DataManagement, "Moving %d cluster infos forward", numClusterInfosToBeAffected - numClusterInfos);

memmove(clusterInfoList, clusterInfoList + numClusterInfos,
sizeof(ClusterInfo) * static_cast<size_t>(numClusterInfosToBeAffected - numClusterInfos));

for (size_t i = 0; i < CHIP_MAX_NUM_READ_HANDLER; ++i)
{
ReadHandler * const handler = mReadHandlers + i;

if ((apReadHandler != handler) && (handler->GetClusterInfoList() > clusterInfoList))
{
handler->ShrinkClusterInfo(numClusterInfos);
}
}
}

// The default implementation to make compiler happy before codegen for this is ready.
// TODO: Remove this after codegen is ready.
void __attribute__((weak))
Expand All @@ -240,9 +292,43 @@ DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aComman
"Default DispatchSingleClusterCommand is called, this should be replaced by actual dispatched for cluster commands");
}

CHIP_ERROR __attribute__((weak))
ReadSingleClusterData(NodeId aNodeId, ClusterId aClusterId, EndpointId aEndPointId, FieldId aFieldId, TLV::TLVWriter & aWriter)
{
ChipLogDetail(DataManagement,
"Received Cluster Command: Cluster=%" PRIx16 " NodeId=%" PRIx64 " Endpoint=%" PRIx8 " FieldId=%" PRIx8,
aClusterId, aNodeId, aEndPointId, aFieldId);
ChipLogError(DataManagement,
"Default ReadSingleClusterData is called, this should be replaced by actual dispatched for cluster");
return CHIP_NO_ERROR;
}

CHIP_ERROR __attribute__((weak))
WriteSingleClusterData(NodeId aNodeId, ClusterId aClusterId, EndpointId aEndPointId, FieldId aFieldId, TLV::TLVReader & aReader)
{
ChipLogDetail(DataManagement,
"Received Cluster Command: Cluster=%" PRIx16 " NodeId=%" PRIx64 " Endpoint=%" PRIx8 " FieldId=%" PRIx8,
aClusterId, aNodeId, aEndPointId, aFieldId);
ChipLogError(DataManagement,
"Default WriteSingleClusterData is called, this should be replaced by actual dispatched for cluster");
return CHIP_NO_ERROR;
}

uint16_t InteractionModelEngine::GetReadClientArrayIndex(const ReadClient * const apReadClient) const
{
return static_cast<uint16_t>(apReadClient - mReadClients);
}

CHIP_ERROR InteractionModelEngine::GetFirstAvailableClusterInfo(ClusterInfo *& apClusterInfo)
{
if (mNumClusterInfos >= IM_SERVER_MAX_NUM_PATH_GROUPS)
{
return CHIP_ERROR_NO_MEMORY;
}
apClusterInfo = &mClusterInfoPool[mNumClusterInfos];
mNumClusterInfos++;
apClusterInfo->ClearDirty();
return CHIP_NO_ERROR;
}
} // namespace app
} // namespace chip
16 changes: 14 additions & 2 deletions src/app/InteractionModelEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,29 @@
#include <support/logging/CHIPLogging.h>
#include <system/SystemPacketBuffer.h>

#include <app/ClusterInfo.h>
#include <app/Command.h>
#include <app/CommandHandler.h>
#include <app/CommandSender.h>
#include <app/InteractionModelDelegate.h>
#include <app/ReadClient.h>
#include <app/ReadHandler.h>
#include <app/reporting/Engine.h>
#include <app/util/basic-types.h>

#define CHIP_MAX_NUM_COMMAND_HANDLER 1
#define CHIP_MAX_NUM_COMMAND_SENDER 1
#define CHIP_MAX_NUM_READ_CLIENT 1
#define CHIP_MAX_NUM_READ_HANDLER 1
#define CHIP_MAX_REPORTS_IN_FLIGHT 1
#define IM_SERVER_MAX_NUM_PATH_GROUPS 256

namespace chip {
namespace app {

constexpr size_t kMaxSecureSduLengthBytes = 1024;
constexpr uint32_t kImMessageTimeoutMsec = 3000;

constexpr FieldId kRootFieldId = 0;
/**
* @class InteractionModelEngine
*
Expand Down Expand Up @@ -127,6 +130,10 @@ class InteractionModelEngine : public Messaging::ExchangeDelegate

reporting::Engine & GetReportingEngine() { return mReportingEngine; }

void ReleaseClusterInfoListToPool(ReadHandler * const apReadHandler);

CHIP_ERROR GetFirstAvailableClusterInfo(ClusterInfo *& apClusterInfo);

private:
friend class reporting::Engine;
void OnUnknownMsgType(Messaging::ExchangeContext * apExchangeContext, const PacketHeader & aPacketHeader,
Expand All @@ -151,10 +158,15 @@ class InteractionModelEngine : public Messaging::ExchangeDelegate
ReadClient mReadClients[CHIP_MAX_NUM_READ_CLIENT];
ReadHandler mReadHandlers[CHIP_MAX_NUM_READ_HANDLER];
reporting::Engine mReportingEngine;
long mNumClusterInfos = 0;
ClusterInfo mClusterInfoPool[IM_SERVER_MAX_NUM_PATH_GROUPS];
};

void DispatchSingleClusterCommand(chip::ClusterId aClusterId, chip::CommandId aCommandId, chip::EndpointId aEndPointId,
chip::TLV::TLVReader & aReader, Command * apCommandObj);

CHIP_ERROR ReadSingleClusterData(NodeId aNodeId, ClusterId aClusterId, EndpointId aEndPointId, FieldId aFieldId,
TLV::TLVWriter & aWriter);
CHIP_ERROR WriteSingleClusterData(NodeId aNodeId, ClusterId aClusterId, EndpointId aEndPointId, FieldId aFieldId,
TLV::TLVReader & aReader);
} // namespace app
} // namespace chip
133 changes: 107 additions & 26 deletions src/app/ReadClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ void ReadClient::MoveToState(const ClientState aTargetState)
}

CHIP_ERROR ReadClient::SendReadRequest(NodeId aNodeId, Transport::AdminId aAdminId, EventPathParams * apEventPathParamsList,
size_t aEventPathParamsListSize)
size_t aEventPathParamsListSize, AttributePathParams * apAttributePathParamsList,
size_t aAttributePathParamsListSize)
{
CHIP_ERROR err = CHIP_NO_ERROR;
System::PacketBufferHandle msgBuf;
Expand All @@ -104,6 +105,33 @@ CHIP_ERROR ReadClient::SendReadRequest(NodeId aNodeId, Transport::AdminId aAdmin
{
// TODO: fill to construct event paths
}

if (aAttributePathParamsListSize != 0 && apAttributePathParamsList != nullptr)
{
AttributePathList::Builder attributePathListBuilder = request.CreateAttributePathListBuilder();
SuccessOrExit(attributePathListBuilder.GetError());
for (size_t index = 0; index < aAttributePathParamsListSize; index++)
{
AttributePath::Builder attributePathBuilder = attributePathListBuilder.CreateAttributePathBuilder();
attributePathBuilder.NodeId(apAttributePathParamsList[index].mNodeId)
.EndpointId(apAttributePathParamsList[index].mEndpointId)
.ClusterId(apAttributePathParamsList[index].mClusterId);
if (apAttributePathParamsList[index].mFlags == AttributePathFlags::kFieldIdValid)
{
attributePathBuilder.FieldId(apAttributePathParamsList[index].mFieldId);
}
else if (apAttributePathParamsList[index].mFlags == AttributePathFlags::kListIndexValid)
{
attributePathBuilder.ListIndex(apAttributePathParamsList[index].mListIndex);
}
else
{
err = CHIP_ERROR_INVALID_ARGUMENT;
ExitNow();
}
SuccessOrExit(attributePathBuilder.GetError());
}
}
request.EndOfReadRequest();
SuccessOrExit(request.GetError());

Expand Down Expand Up @@ -139,7 +167,6 @@ void ReadClient::OnMessageReceived(Messaging::ExchangeContext * apExchangeContex

ClearExistingExchangeContext();
MoveToState(ClientState::Initialized);

if (mpDelegate != nullptr)
{
if (err != CHIP_NO_ERROR)
Expand Down Expand Up @@ -171,10 +198,12 @@ CHIP_ERROR ReadClient::ProcessReportData(System::PacketBufferHandle aPayload)
CHIP_ERROR err = CHIP_NO_ERROR;
ReportData::Parser report;

bool isEventListPresent = false;
bool suppressResponse = false;
bool moreChunkedMessages = false;

bool isEventListPresent = false;
bool isAttributeDataListPresent = false;
bool suppressResponse = false;
bool moreChunkedMessages = false;
EventList::Parser eventList;
AttributeDataList::Parser attributeDataList;
System::PacketBufferTLVReader reader;

reader.Init(std::move(aPayload));
Expand Down Expand Up @@ -202,30 +231,35 @@ CHIP_ERROR ReadClient::ProcessReportData(System::PacketBufferHandle aPayload)
}
SuccessOrExit(err);

err = report.GetEventDataList(&eventList);
isEventListPresent = (err == CHIP_NO_ERROR);
if (err == CHIP_END_OF_TLV)
{
EventList::Parser eventList;
err = CHIP_NO_ERROR;
}
SuccessOrExit(err);

err = report.GetEventDataList(&eventList);
if (CHIP_NO_ERROR == err)
{
isEventListPresent = true;
}
else if (CHIP_END_OF_TLV == err)
{
isEventListPresent = false;
err = CHIP_NO_ERROR;
}
if (isEventListPresent && nullptr != mpDelegate)
{
chip::TLV::TLVReader eventListReader;
eventList.GetReader(&eventListReader);
err = mpDelegate->EventStreamReceived(mpExchangeCtx, &eventListReader);
SuccessOrExit(err);
}

VerifyOrExit(moreChunkedMessages == false, err = CHIP_ERROR_MESSAGE_INCOMPLETE);

if (isEventListPresent && nullptr != mpDelegate)
{
chip::TLV::TLVReader eventListReader;
eventList.GetReader(&eventListReader);
err = mpDelegate->EventStreamReceived(mpExchangeCtx, &eventListReader);
SuccessOrExit(err);
}
err = report.GetAttributeDataList(&attributeDataList);
isAttributeDataListPresent = (err == CHIP_NO_ERROR);
if (err == CHIP_END_OF_TLV)
{
err = CHIP_NO_ERROR;
}
SuccessOrExit(err);
if (isAttributeDataListPresent && nullptr != mpDelegate && !moreChunkedMessages)
{
chip::TLV::TLVReader attributeDataListReader;
attributeDataList.GetReader(&attributeDataListReader);
err = ProcessAttributeDataList(attributeDataListReader);
SuccessOrExit(err);
}

if (!suppressResponse)
Expand All @@ -250,5 +284,52 @@ void ReadClient::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContex
mpDelegate->ReportError(this, CHIP_ERROR_TIMEOUT);
}
}

CHIP_ERROR ReadClient::ProcessAttributeDataList(TLV::TLVReader & aAttributeDataListReader)
{
CHIP_ERROR err = CHIP_NO_ERROR;
while (CHIP_NO_ERROR == (err = aAttributeDataListReader.Next()))
{
NodeId nodeId = 0;
EndpointId endpointId = 0;
ClusterId clusterId = 0;
FieldId fieldId = 0;
chip::TLV::TLVReader dataReader;
AttributeDataElement::Parser element;
AttributePath::Parser attributePathParser;
TLV::TLVReader reader = aAttributeDataListReader;
err = element.Init(reader);
SuccessOrExit(err);

err = element.GetAttributePath(&attributePathParser);
SuccessOrExit(err);

err = attributePathParser.GetNodeId(&nodeId);
SuccessOrExit(err);

err = attributePathParser.GetEndpointId(&endpointId);
SuccessOrExit(err);

err = attributePathParser.GetClusterId(&clusterId);
SuccessOrExit(err);

err = attributePathParser.GetFieldId(&fieldId);
SuccessOrExit(err);

err = element.GetData(&dataReader);
SuccessOrExit(err);
err = WriteSingleClusterData(nodeId, clusterId, endpointId, fieldId, dataReader);
SuccessOrExit(err);
}

if (CHIP_END_OF_TLV == err)
{
err = CHIP_NO_ERROR;
}

exit:
ChipLogFunctError(err);
return err;
}
}; // namespace app
}; // namespace chip
7 changes: 6 additions & 1 deletion src/app/ReadClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,14 @@ class ReadClient : public Messaging::ExchangeDelegate
* @param[in] aAdminId Admin ID
* @param[in] apEventPathParamsList a list of event paths the read client is interested in
* @param[in] aEventPathParamsListSize Number of event paths in apEventPathParamsList
* @param[in] apAttributePathParamsList a list of attribute paths the read client is interested in
* @param[in] aAttributePathParamsListSize Number of attribute paths in apAttributePathParamsList
* @retval #others fail to send read request
* @retval #CHIP_NO_ERROR On success.
*/
CHIP_ERROR SendReadRequest(NodeId aNodeId, Transport::AdminId aAdminId, EventPathParams * apEventPathParamsList,
size_t aEventPathParamsListSize);
size_t aEventPathParamsListSize, AttributePathParams * apAttributePathParamsList,
size_t aAttributePathParamsListSize);

private:
friend class TestReadInteraction;
Expand Down Expand Up @@ -114,6 +117,8 @@ class ReadClient : public Messaging::ExchangeDelegate
*/
bool IsFree() const { return mState == ClientState::Uninitialized; };

CHIP_ERROR ProcessAttributeDataList(TLV::TLVReader & aAttributeDataListReader);

void MoveToState(const ClientState aTargetState);
CHIP_ERROR ProcessReportData(System::PacketBufferHandle aPayload);
CHIP_ERROR ClearExistingExchangeContext();
Expand Down
Loading

0 comments on commit 58ba3fb

Please sign in to comment.