Skip to content

Commit

Permalink
[IM] Notify AttributeAccessInterface for end of list (#16670)
Browse files Browse the repository at this point in the history
* [IM] Notify AttributeAccessInterface for end of list

* Address comments

* Fix

* Resolve iOS build issue

* Clean & Restyle

* Address comments

* Address comments, fixes the case related to nullable lists

* Remove redundant comment and clearify the comment

* Address comments

* Address comments and reformat

* Fix dirty merge, try to reduce rom usage

* Try to save code size
  • Loading branch information
erjiaqing authored and pull[bot] committed Aug 15, 2023
1 parent 60b1d10 commit 2090097
Show file tree
Hide file tree
Showing 18 changed files with 610 additions and 58 deletions.
27 changes: 27 additions & 0 deletions src/app/AttributeAccessInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,33 @@ class AttributeAccessInterface
*/
virtual CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) { return CHIP_NO_ERROR; }

/**
* Indicates the start of a series of list operations. This function will be called before the first Write operation of a series
* of consequence attribute data of the same attribute.
*
* 1) This function will be called if the client tries to set a nullable list attribute to null.
* 2) This function will only be called once for a series of consequent attribute data (regardless the kind of list operation)
* of the same attribute.
*
* @param [in] aPath indicates the path of the modified list.
*/
virtual void OnListWriteBegin(const ConcreteAttributePath & aPath) {}

/**
* Indicates the end of a series of list operations. This function will be called after the last Write operation of a series
* of consequence attribute data of the same attribute.
*
* 1) This function will be called if the client tries to set a nullable list attribute to null.
* 2) This function will only be called once for a series of consequent attribute data (regardless the kind of list operation)
* of the same attribute.
* 3) When aWriteWasSuccessful is true, the data written must be consistent or the list is untouched.
*
* @param [in] aPath indicates the path of the modified list
* @param [in] aWriteWasSuccessful indicates whether the delivered list is complete.
*
*/
virtual void OnListWriteEnd(const ConcreteAttributePath & aPath, bool aWriteWasSuccessful) {}

/**
* Mechanism for keeping track of a chain of AttributeAccessInterfaces.
*/
Expand Down
14 changes: 7 additions & 7 deletions src/app/ChunkedWriteCallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ void ChunkedWriteCallback::OnResponse(const WriteClient * apWriteClient, const C
{
// We may send a chunked list. To make the behavior consistent whether a list is being chunked or not,
// we merge the write responses for a chunked list here and provide our consumer with a single status response.
if (mLastAttributePath.HasValue())
if (mProcessingAttributePath.HasValue())
{
// This is not the first write response.
if (IsAppendingToLastItem(aPath))
Expand All @@ -40,7 +40,7 @@ void ChunkedWriteCallback::OnResponse(const WriteClient * apWriteClient, const C
}

// This is a response to another attribute write. Report the final result of last attribute write.
callback->OnResponse(apWriteClient, mLastAttributePath.Value(), mAttributeStatus);
callback->OnResponse(apWriteClient, mProcessingAttributePath.Value(), mAttributeStatus);
}

// This is the first report for a new attribute. We assume it will never be a list item operation.
Expand All @@ -49,7 +49,7 @@ void ChunkedWriteCallback::OnResponse(const WriteClient * apWriteClient, const C
aStatus = StatusIB(CHIP_ERROR_INCORRECT_STATE);
}

mLastAttributePath.SetValue(aPath);
mProcessingAttributePath.SetValue(aPath);
mAttributeStatus = aStatus;
// For the last status in the response, we will call the application callback in OnDone()
}
Expand All @@ -61,12 +61,12 @@ void ChunkedWriteCallback::OnError(const WriteClient * apWriteClient, CHIP_ERROR

void ChunkedWriteCallback::OnDone(WriteClient * apWriteClient)
{
if (mLastAttributePath.HasValue())
if (mProcessingAttributePath.HasValue())
{
// We have a cached status that has yet to be reported to the application so report it now.
// If we failed to receive the response, or we received a malformed response, OnResponse won't be called,
// mLastAttributePath will be Missing() in this case.
callback->OnResponse(apWriteClient, mLastAttributePath.Value(), mAttributeStatus);
// mProcessingAttributePath will be Missing() in this case.
callback->OnResponse(apWriteClient, mProcessingAttributePath.Value(), mAttributeStatus);
}

callback->OnDone(apWriteClient);
Expand All @@ -78,7 +78,7 @@ bool ChunkedWriteCallback::IsAppendingToLastItem(const ConcreteDataAttributePath
{
return false;
}
if (!mLastAttributePath.HasValue() || !(mLastAttributePath.Value() == aPath))
if (!mProcessingAttributePath.HasValue() || !(mProcessingAttributePath.Value() == aPath))
{
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/ChunkedWriteCallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ChunkedWriteCallback : public WriteClient::Callback

// We are using the casts between ConcreteAttributePath and ConcreteDataAttributePath, then all paths passed to upper
// applications will always have NotList as mListOp.
Optional<ConcreteAttributePath> mLastAttributePath;
Optional<ConcreteAttributePath> mProcessingAttributePath;
StatusIB mAttributeStatus;

WriteClient::Callback * callback;
Expand Down
2 changes: 2 additions & 0 deletions src/app/ConcreteAttributePath.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ struct ConcreteAttributePath : public ConcreteClusterPath
return ConcreteClusterPath::operator==(aOther) && (mAttributeId == aOther.mAttributeId);
}

bool operator!=(const ConcreteAttributePath & aOther) const { return !(*this == aOther); }

bool operator<(const ConcreteAttributePath & path) const
{
return (mEndpointId < path.mEndpointId) || ((mEndpointId == path.mEndpointId) && (mClusterId < path.mClusterId)) ||
Expand Down
6 changes: 3 additions & 3 deletions src/app/InteractionModelEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ CHIP_ERROR InteractionModelEngine::PushFrontAttributePathList(ObjectList<Attribu
CHIP_ERROR err = PushFront(aAttributePathList, aAttributePath, mAttributePathPool);
if (err == CHIP_ERROR_NO_MEMORY)
{
ChipLogError(InteractionModel, "AttributePath pool full, cannot handle more entries!");
ChipLogError(InteractionModel, "AttributePath pool full");
}
return err;
}
Expand All @@ -587,7 +587,7 @@ CHIP_ERROR InteractionModelEngine::PushFrontEventPathParamsList(ObjectList<Event
CHIP_ERROR err = PushFront(aEventPathList, aEventPath, mEventPathPool);
if (err == CHIP_ERROR_NO_MEMORY)
{
ChipLogError(InteractionModel, "EventPath pool full, cannot handle more entries!");
ChipLogError(InteractionModel, "EventPath pool full");
}
return err;
}
Expand All @@ -603,7 +603,7 @@ CHIP_ERROR InteractionModelEngine::PushFrontDataVersionFilterList(ObjectList<Dat
CHIP_ERROR err = PushFront(aDataVersionFilterList, aDataVersionFilter, mDataVersionFilterPool);
if (err == CHIP_ERROR_NO_MEMORY)
{
ChipLogError(InteractionModel, "DataVersionFilter pool full, cannot handle more entries, reset this error and continue!");
ChipLogError(InteractionModel, "DataVersionFilter pool full, ignore this filter");
err = CHIP_NO_ERROR;
}
return err;
Expand Down
15 changes: 15 additions & 0 deletions src/app/InteractionModelEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include <app/WriteClient.h>
#include <app/WriteHandler.h>
#include <app/reporting/Engine.h>
#include <app/util/attribute-metadata.h>
#include <app/util/basic-types.h>

namespace chip {
Expand Down Expand Up @@ -380,6 +381,13 @@ CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescr
const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports,
AttributeValueEncoder::AttributeEncodeState * apEncoderState);

/**
* Get the registered attribute access override. nullptr when attribute access override is not found.
*
* TODO(#16806): This function and registerAttributeAccessOverride can be member functions of InteractionModelEngine.
*/
AttributeAccessInterface * GetAttributeAccessOverride(EndpointId aEndpointId, ClusterId aClusterId);

/**
* TODO: Document.
*/
Expand All @@ -397,5 +405,12 @@ bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath,
*/
bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint);

/**
* Returns the metadata of the attribute for the given path.
*
* @retval The metadata of the attribute, will return null if the given attribute does not exists.
*/
const EmberAfAttributeMetadata * GetAttributeMetadata(const ConcreteAttributePath & aConcreteClusterPath);

} // namespace app
} // namespace chip
Loading

0 comments on commit 2090097

Please sign in to comment.