Add DataVersionFilter for read/subscribe (#14711)
* Add DataVersionFilter support for read/subscribe
diff --git a/src/app/AttributeCache.cpp b/src/app/AttributeCache.cpp
index 35a9b70..349a856 100644
--- a/src/app/AttributeCache.cpp
+++ b/src/app/AttributeCache.cpp
@@ -100,8 +100,7 @@
mCallback.OnReportEnd();
}
-void AttributeCache::OnAttributeData(const ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
- const StatusIB & aStatus)
+void AttributeCache::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus)
{
//
// Since the cache itself is a ReadClient::Callback, it may be incorrectly passed in directly when registering with the
@@ -121,7 +120,7 @@
//
// Forward the call through.
//
- mCallback.OnAttributeData(aPath, aVersion, apData, aStatus);
+ mCallback.OnAttributeData(aPath, apData, aStatus);
}
CHIP_ERROR AttributeCache::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader)
diff --git a/src/app/AttributeCache.h b/src/app/AttributeCache.h
index 1e682d2..907dbed 100644
--- a/src/app/AttributeCache.h
+++ b/src/app/AttributeCache.h
@@ -342,8 +342,7 @@
//
void OnReportBegin() override;
void OnReportEnd() override;
- void OnAttributeData(const ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
- const StatusIB & aStatus) override;
+ void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) override;
void OnError(CHIP_ERROR aError) override { return mCallback.OnError(aError); }
void OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData, const StatusIB * apStatus) override
diff --git a/src/app/BufferedReadCallback.cpp b/src/app/BufferedReadCallback.cpp
index b268691..e1188a4 100644
--- a/src/app/BufferedReadCallback.cpp
+++ b/src/app/BufferedReadCallback.cpp
@@ -215,7 +215,7 @@
//
ReturnErrorOnFailure(reader.Next());
- mCallback.OnAttributeData(mBufferedPath, mDataVersion, &reader, statusIB);
+ mCallback.OnAttributeData(mBufferedPath, &reader, statusIB);
//
// Clear out our buffered contents to free up allocated buffers, and reset the buffered path.
@@ -225,7 +225,7 @@
return CHIP_NO_ERROR;
}
-void BufferedReadCallback::OnAttributeData(const ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
+void BufferedReadCallback::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
const StatusIB & aStatus)
{
CHIP_ERROR err;
@@ -246,14 +246,13 @@
}
else
{
- mCallback.OnAttributeData(aPath, aVersion, apData, aStatus);
+ mCallback.OnAttributeData(aPath, apData, aStatus);
}
//
// Update our latched buffered path.
//
mBufferedPath = aPath;
- mDataVersion = aVersion;
exit:
if (err != CHIP_NO_ERROR)
diff --git a/src/app/BufferedReadCallback.h b/src/app/BufferedReadCallback.h
index ce9d959..5f88ebd 100644
--- a/src/app/BufferedReadCallback.h
+++ b/src/app/BufferedReadCallback.h
@@ -69,8 +69,7 @@
//
void OnReportBegin() override;
void OnReportEnd() override;
- void OnAttributeData(const ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
- const StatusIB & aStatus) override;
+ void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) override;
void OnError(CHIP_ERROR aError) override { return mCallback.OnError(aError); }
void OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData, const StatusIB * apStatus) override
{
@@ -94,7 +93,6 @@
*
*/
CHIP_ERROR BufferListItem(TLV::TLVReader & reader);
- DataVersion mDataVersion;
ConcreteDataAttributePath mBufferedPath;
std::vector<System::PacketBufferHandle> mBufferedList;
Callback & mCallback;
diff --git a/src/app/ClusterInfo.h b/src/app/ClusterInfo.h
index 55e7d7e..e8fdc95 100644
--- a/src/app/ClusterInfo.h
+++ b/src/app/ClusterInfo.h
@@ -79,6 +79,8 @@
// For event, an event id can only be interpreted if the cluster id is known.
bool IsValidEventPath() const { return !(HasWildcardClusterId() && !HasWildcardEventId()); }
+ bool IsValidDataVersionFilter() const { return !HasWildcardEndpointId() && !HasWildcardClusterId() && mDataVersion.HasValue(); }
+
inline bool HasWildcardNodeId() const { return mNodeId == kUndefinedNodeId; }
inline bool HasWildcardEndpointId() const { return mEndpointId == kInvalidEndpointId; }
inline bool HasWildcardClusterId() const { return mClusterId == kInvalidClusterId; }
@@ -100,6 +102,7 @@
EventId mEventId = kInvalidEventId; // uint32
ListIndex mListIndex = kInvalidListIndex; // uint16
EndpointId mEndpointId = kInvalidEndpointId; // uint16
+ Optional<DataVersion> mDataVersion; // uint32
};
} // namespace app
} // namespace chip
diff --git a/src/app/ConcreteAttributePath.h b/src/app/ConcreteAttributePath.h
index 97e29bc..486c5dc 100644
--- a/src/app/ConcreteAttributePath.h
+++ b/src/app/ConcreteAttributePath.h
@@ -111,6 +111,10 @@
ConcreteAttributePath(aEndpointId, aClusterId, aAttributeId)
{}
+ ConcreteDataAttributePath(EndpointId aEndpointId, ClusterId aClusterId, AttributeId aAttributeId, DataVersion aDataVersion) :
+ ConcreteAttributePath(aEndpointId, aClusterId, aAttributeId), mDataVersion(aDataVersion)
+ {}
+
ConcreteDataAttributePath(EndpointId aEndpointId, ClusterId aClusterId, AttributeId aAttributeId, ListOperation aListOp,
uint16_t aListIndex) :
ConcreteAttributePath(aEndpointId, aClusterId, aAttributeId)
@@ -126,8 +130,9 @@
// This index is only valid if `mListOp` is set to a list item operation, i.e
// ReplaceItem, DeleteItem or AppendItem. Otherwise, it is to be ignored.
//
- uint16_t mListIndex = 0;
- ListOperation mListOp = ListOperation::NotList;
+ uint16_t mListIndex = 0;
+ ListOperation mListOp = ListOperation::NotList;
+ DataVersion mDataVersion = 0;
};
} // namespace app
diff --git a/src/app/DataVersionFilter.h b/src/app/DataVersionFilter.h
new file mode 100644
index 0000000..ba93de6
--- /dev/null
+++ b/src/app/DataVersionFilter.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright (c) 2022 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/util/basic-types.h>
+
+#include <app/ClusterInfo.h>
+
+namespace chip {
+namespace app {
+struct DataVersionFilter
+{
+ DataVersionFilter(EndpointId aEndpointId, ClusterId aClusterId, DataVersion aDataVersion) :
+ mEndpointId(aEndpointId), mClusterId(aClusterId), mDataVersion(aDataVersion)
+ {}
+
+ DataVersionFilter() {}
+
+ bool IsValidDataVersionFilter()
+ {
+ return (mEndpointId != kInvalidEndpointId) && (mClusterId != kInvalidClusterId) && (mDataVersion.HasValue());
+ }
+
+ EndpointId mEndpointId = kInvalidEndpointId;
+ ClusterId mClusterId = kInvalidClusterId;
+ Optional<DataVersion> mDataVersion;
+};
+} // namespace app
+} // namespace chip
diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h
index 78b936b..6a7aa18 100644
--- a/src/app/InteractionModelEngine.h
+++ b/src/app/InteractionModelEngine.h
@@ -340,5 +340,10 @@
CHIP_ERROR WriteSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor,
const ConcreteDataAttributePath & aAttributePath, TLV::TLVReader & aReader,
WriteHandler * apWriteHandler);
+
+/**
+ * Check if the given cluster has the given DataVersion.
+ */
+bool IsClusterDataVersionEqual(EndpointId aEndpointId, ClusterId aClusterId, DataVersion aRequiredVersion);
} // namespace app
} // namespace chip
diff --git a/src/app/MessageDef/ReadRequestMessage.cpp b/src/app/MessageDef/ReadRequestMessage.cpp
index 9747882..97d2a2a 100644
--- a/src/app/MessageDef/ReadRequestMessage.cpp
+++ b/src/app/MessageDef/ReadRequestMessage.cpp
@@ -59,7 +59,7 @@
break;
case to_underlying(Tag::kDataVersionFilters):
// check if this tag has appeared before
- VerifyOrReturnError(!(tagPresenceMask & (1 << to_underlying(Tag::kEventFilters))), CHIP_ERROR_INVALID_TLV_TAG);
+ VerifyOrReturnError(!(tagPresenceMask & (1 << to_underlying(Tag::kDataVersionFilters))), CHIP_ERROR_INVALID_TLV_TAG);
tagPresenceMask |= (1 << to_underlying(Tag::kDataVersionFilters));
{
DataVersionFilterIBs::Parser dataVersionFilters;
diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp
index 7c0047b..e88aa7c 100644
--- a/src/app/ReadClient.cpp
+++ b/src/app/ReadClient.cpp
@@ -222,6 +222,14 @@
ReturnErrorOnFailure(err = request.GetError());
ReturnErrorOnFailure(GenerateAttributePathList(attributePathListBuilder, aReadPrepareParams.mpAttributePathParamsList,
aReadPrepareParams.mAttributePathParamsListSize));
+ if (aReadPrepareParams.mDataVersionFilterListSize != 0 && aReadPrepareParams.mpDataVersionFilterList != nullptr)
+ {
+ DataVersionFilterIBs::Builder & dataVersionFilterListBuilder = request.CreateDataVersionFilters();
+ ReturnErrorOnFailure(request.GetError());
+ ReturnErrorOnFailure(GenerateDataVersionFilterList(dataVersionFilterListBuilder,
+ aReadPrepareParams.mpDataVersionFilterList,
+ aReadPrepareParams.mDataVersionFilterListSize));
+ }
}
if (aReadPrepareParams.mEventPathParamsListSize != 0 && aReadPrepareParams.mpEventPathParamsList != nullptr)
@@ -296,6 +304,28 @@
return aAttributePathIBsBuilder.GetError();
}
+CHIP_ERROR ReadClient::GenerateDataVersionFilterList(DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder,
+ DataVersionFilter * apDataVersionFilterList, size_t aDataVersionFilterListSize)
+{
+ for (size_t index = 0; index < aDataVersionFilterListSize; index++)
+ {
+ VerifyOrReturnError(apDataVersionFilterList[index].IsValidDataVersionFilter(), CHIP_ERROR_INVALID_ARGUMENT);
+ DataVersionFilterIB::Builder & filter = aDataVersionFilterIBsBuilder.CreateDataVersionFilter();
+ ReturnErrorOnFailure(aDataVersionFilterIBsBuilder.GetError());
+ ClusterPathIB::Builder & path = filter.CreatePath();
+ ReturnErrorOnFailure(filter.GetError());
+ ReturnErrorOnFailure(path.Endpoint(apDataVersionFilterList[index].mEndpointId)
+ .Cluster(apDataVersionFilterList[index].mClusterId)
+ .EndOfClusterPathIB()
+ .GetError());
+ VerifyOrReturnError(apDataVersionFilterList[index].mDataVersion.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
+ ReturnErrorOnFailure(
+ filter.DataVersion(apDataVersionFilterList[index].mDataVersion.Value()).EndOfDataVersionFilterIB().GetError());
+ }
+
+ return aDataVersionFilterIBsBuilder.EndOfDataVersionFilterIBs().GetError();
+}
+
CHIP_ERROR ReadClient::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
System::PacketBufferHandle && aPayload)
{
@@ -554,8 +584,7 @@
TLV::TLVReader reader = aAttributeReportIBsReader;
ReturnErrorOnFailure(report.Init(reader));
- DataVersion version = kUndefinedDataVersion;
- err = report.GetAttributeStatus(&status);
+ err = report.GetAttributeStatus(&status);
if (CHIP_NO_ERROR == err)
{
StatusIB::Parser errorStatus;
@@ -563,14 +592,19 @@
ReturnErrorOnFailure(ProcessAttributePath(path, attributePath));
ReturnErrorOnFailure(status.GetErrorStatus(&errorStatus));
ReturnErrorOnFailure(errorStatus.DecodeStatusIB(statusIB));
- mpCallback.OnAttributeData(attributePath, version, nullptr, statusIB);
+ mpCallback.OnAttributeData(attributePath, nullptr, statusIB);
}
else if (CHIP_END_OF_TLV == err)
{
ReturnErrorOnFailure(report.GetAttributeData(&data));
ReturnErrorOnFailure(data.GetPath(&path));
ReturnErrorOnFailure(ProcessAttributePath(path, attributePath));
- ReturnErrorOnFailure(data.GetDataVersion(&version));
+ ReturnErrorOnFailure(data.GetDataVersion(&attributePath.mDataVersion));
+ if (mReadPrepareParams.mResubscribePolicy != nullptr)
+ {
+ UpdateDataVersionFilters(attributePath);
+ }
+
ReturnErrorOnFailure(data.GetData(&dataReader));
// The element in an array may be another array -- so we should only set the list operation when we are handling the
@@ -580,7 +614,7 @@
attributePath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
}
- mpCallback.OnAttributeData(attributePath, version, &dataReader, statusIB);
+ mpCallback.OnAttributeData(attributePath, &dataReader, statusIB);
}
}
@@ -749,6 +783,14 @@
ReturnErrorOnFailure(err = attributePathListBuilder.GetError());
ReturnErrorOnFailure(GenerateAttributePathList(attributePathListBuilder, aReadPrepareParams.mpAttributePathParamsList,
aReadPrepareParams.mAttributePathParamsListSize));
+ if (aReadPrepareParams.mDataVersionFilterListSize != 0 && aReadPrepareParams.mpDataVersionFilterList != nullptr)
+ {
+ DataVersionFilterIBs::Builder & dataVersionFilterListBuilder = request.CreateDataVersionFilters();
+ ReturnErrorOnFailure(request.GetError());
+ ReturnErrorOnFailure(GenerateDataVersionFilterList(dataVersionFilterListBuilder,
+ aReadPrepareParams.mpDataVersionFilterList,
+ aReadPrepareParams.mDataVersionFilterListSize));
+ }
}
if (aReadPrepareParams.mEventPathParamsListSize != 0 && aReadPrepareParams.mpEventPathParamsList != nullptr)
@@ -775,7 +817,6 @@
request.IsFabricFiltered(aReadPrepareParams.mIsFabricFiltered).EndOfSubscribeRequestMessage();
ReturnErrorOnFailure(err = request.GetError());
-
ReturnErrorOnFailure(writer.Finalize(&msgBuf));
mpExchangeCtx = mpExchangeMgr->NewContext(aReadPrepareParams.mSessionHolder.Get(), this);
@@ -831,5 +872,17 @@
return true;
}
+void ReadClient::UpdateDataVersionFilters(const ConcreteDataAttributePath & aPath)
+{
+ for (size_t index = 0; index < mReadPrepareParams.mDataVersionFilterListSize; index++)
+ {
+ if (mReadPrepareParams.mpDataVersionFilterList[index].mEndpointId == aPath.mEndpointId &&
+ mReadPrepareParams.mpDataVersionFilterList[index].mClusterId == aPath.mClusterId)
+ {
+ // Now we know the current version for this cluster is aPath.mDataVersion.
+ mReadPrepareParams.mpDataVersionFilterList[index].mDataVersion.SetValue(aPath.mDataVersion);
+ }
+ }
+}
} // namespace app
} // namespace chip
diff --git a/src/app/ReadClient.h b/src/app/ReadClient.h
index 24f731e..c8f30f3 100644
--- a/src/app/ReadClient.h
+++ b/src/app/ReadClient.h
@@ -110,14 +110,11 @@
* receives an OnDone call to destroy the object.
*
* @param[in] aPath The attribute path field in report response.
- * @param[in] aVersion The data version for cluster 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 ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
- const StatusIB & aStatus)
- {}
+ virtual void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) {}
/**
* OnSubscriptionEstablished will be called when a subscription is established for the given subscription transaction.
@@ -250,13 +247,13 @@
// used.
//
// The application has to know to
- // a) allocate a ReadPrepareParams object that will have fields mpEventPathParamsList and mpAttributePathParamsList with
- // lifetimes as long as the ReadClient itself and b) free those up later in the call to OnDeallocatePaths. Note: At a given
- // time in the system, you can either have a single subscription with re-sub enabled that that has mKeepSubscriptions = false,
- // OR, multiple subs with re-sub enabled with mKeepSubscriptions = true. You shall not have a mix of both simultaneously.
- // If SendAutoResubscribeRequest is called at all, it guarantees that it will call OnDeallocatePaths when OnDone is called.
- // SendAutoResubscribeRequest is the only case that calls OnDeallocatePaths, since that's the only case when the consumer moved
- // a ReadParams into the client.
+ // a) allocate a ReadPrepareParams object that will have fields mpEventPathParamsList and mpAttributePathParamsList and
+ // mpDataVersionFilterList with lifetimes as long as the ReadClient itself and b) free those up later in the call to
+ // OnDeallocatePaths. Note: At a given time in the system, you can either have a single subscription with re-sub enabled that
+ // that has mKeepSubscriptions = false, OR, multiple subs with re-sub enabled with mKeepSubscriptions = true. You shall not have
+ // a mix of both simultaneously. If SendAutoResubscribeRequest is called at all, it guarantees that it will call
+ // OnDeallocatePaths when OnDone is called. SendAutoResubscribeRequest is the only case that calls OnDeallocatePaths, since
+ // that's the only case when the consumer moved a ReadParams into the client.
CHIP_ERROR SendAutoResubscribeRequest(ReadPrepareParams && aReadPrepareParams);
private:
@@ -293,6 +290,8 @@
size_t aEventPathParamsListSize);
CHIP_ERROR GenerateAttributePathList(AttributePathIBs::Builder & aAttributePathIBsBuilder,
AttributePathParams * apAttributePathParamsList, size_t aAttributePathParamsListSize);
+ CHIP_ERROR GenerateDataVersionFilterList(DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder,
+ DataVersionFilter * apDataVersionFilterList, size_t aDataVersionFilterListSize);
CHIP_ERROR ProcessAttributeReportIBs(TLV::TLVReader & aAttributeDataIBsReader);
CHIP_ERROR ProcessEventReportIBs(TLV::TLVReader & aEventReportIBsReader);
@@ -310,7 +309,7 @@
// Specialized request-sending functions.
CHIP_ERROR SendReadRequest(ReadPrepareParams & aReadPrepareParams);
CHIP_ERROR SendSubscribeRequest(ReadPrepareParams & aSubscribePrepareParams);
-
+ void UpdateDataVersionFilters(const ConcreteDataAttributePath & aPath);
static void OnResubscribeTimerCallback(System::Layer * apSystemLayer, void * apAppState);
/*
diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp
index 0f49aaa..9d3eeed 100644
--- a/src/app/ReadHandler.cpp
+++ b/src/app/ReadHandler.cpp
@@ -98,6 +98,7 @@
InteractionModelEngine::GetInstance()->ReleaseClusterInfoList(mpAttributeClusterInfoList);
InteractionModelEngine::GetInstance()->ReleaseClusterInfoList(mpEventClusterInfoList);
+ InteractionModelEngine::GetInstance()->ReleaseClusterInfoList(mpDataVersionFilterList);
}
void ReadHandler::Close()
@@ -296,7 +297,6 @@
#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK
ReturnErrorOnFailure(readRequestParser.CheckSchemaValidity());
#endif
-
err = readRequestParser.GetAttributeRequests(&attributePathListParser);
if (err == CHIP_END_OF_TLV)
{
@@ -305,6 +305,16 @@
else if (err == CHIP_NO_ERROR)
{
ReturnErrorOnFailure(ProcessAttributePathList(attributePathListParser));
+ DataVersionFilterIBs::Parser dataVersionFilterListParser;
+ err = readRequestParser.GetDataVersionFilters(&dataVersionFilterListParser);
+ if (err == CHIP_END_OF_TLV)
+ {
+ err = CHIP_NO_ERROR;
+ }
+ else if (err == CHIP_NO_ERROR)
+ {
+ ReturnErrorOnFailure(ProcessDataVersionFilterList(dataVersionFilterListParser));
+ }
}
ReturnErrorOnFailure(err);
err = readRequestParser.GetEventRequests(&eventPathListParser);
@@ -356,7 +366,6 @@
while (CHIP_NO_ERROR == (err = reader.Next()))
{
VerifyOrExit(TLV::AnonymousTag() == reader.GetTag(), err = CHIP_ERROR_INVALID_TLV_TAG);
- VerifyOrExit(TLV::kTLVType_List == reader.GetType(), err = CHIP_ERROR_WRONG_TLV_TYPE);
ClusterInfo clusterInfo;
AttributePathIB::Parser path;
err = path.Init(reader);
@@ -421,6 +430,36 @@
return err;
}
+CHIP_ERROR ReadHandler::ProcessDataVersionFilterList(DataVersionFilterIBs::Parser & aDataVersionFilterListParser)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ TLV::TLVReader reader;
+ aDataVersionFilterListParser.GetReader(&reader);
+
+ while (CHIP_NO_ERROR == (err = reader.Next()))
+ {
+ VerifyOrReturnError(TLV::AnonymousTag() == reader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG);
+ ClusterInfo clusterInfo;
+ ClusterPathIB::Parser path;
+ DataVersionFilterIB::Parser filter;
+ ReturnErrorOnFailure(filter.Init(reader));
+ DataVersion version = 0;
+ ReturnErrorOnFailure(filter.GetDataVersion(&version));
+ clusterInfo.mDataVersion.SetValue(version);
+ ReturnErrorOnFailure(filter.GetPath(&path));
+ ReturnErrorOnFailure(path.GetEndpoint(&(clusterInfo.mEndpointId)));
+ ReturnErrorOnFailure(path.GetCluster(&(clusterInfo.mClusterId)));
+ VerifyOrReturnError(clusterInfo.IsValidDataVersionFilter(), CHIP_ERROR_IM_MALFORMED_DATA_VERSION_FILTER_IB);
+ ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->PushFront(mpDataVersionFilterList, clusterInfo));
+ }
+
+ if (CHIP_END_OF_TLV == err)
+ {
+ err = CHIP_NO_ERROR;
+ }
+ return err;
+}
+
CHIP_ERROR ReadHandler::ProcessEventPaths(EventPathIBs::Parser & aEventPathsParser)
{
CHIP_ERROR err = CHIP_NO_ERROR;
@@ -594,6 +633,16 @@
else if (err == CHIP_NO_ERROR)
{
ReturnErrorOnFailure(ProcessAttributePathList(attributePathListParser));
+ DataVersionFilterIBs::Parser dataVersionFilterListParser;
+ err = subscribeRequestParser.GetDataVersionFilters(&dataVersionFilterListParser);
+ if (err == CHIP_END_OF_TLV)
+ {
+ err = CHIP_NO_ERROR;
+ }
+ else if (err == CHIP_NO_ERROR)
+ {
+ ReturnErrorOnFailure(ProcessDataVersionFilterList(dataVersionFilterListParser));
+ }
}
ReturnErrorOnFailure(err);
diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h
index aae661f..7f64082 100644
--- a/src/app/ReadHandler.h
+++ b/src/app/ReadHandler.h
@@ -132,8 +132,10 @@
bool IsGeneratingReports() const { return mState == HandlerState::GeneratingReports; }
bool IsAwaitingReportResponse() const { return mState == HandlerState::AwaitingReportResponse; }
+ CHIP_ERROR ProcessDataVersionFilterList(DataVersionFilterIBs::Parser & aDataVersionFilterListParser);
ClusterInfo * GetAttributeClusterInfolist() { return mpAttributeClusterInfoList; }
ClusterInfo * GetEventClusterInfolist() { return mpEventClusterInfoList; }
+ ClusterInfo * GetDataVersionFilterlist() const { return mpDataVersionFilterList; }
EventNumber & GetEventMin() { return mEventMin; }
PriorityLevel GetCurrentPriority() { return mCurrentPriority; }
@@ -240,6 +242,7 @@
HandlerState mState = HandlerState::Idle;
ClusterInfo * mpAttributeClusterInfoList = nullptr;
ClusterInfo * mpEventClusterInfoList = nullptr;
+ ClusterInfo * mpDataVersionFilterList = nullptr;
PriorityLevel mCurrentPriority = PriorityLevel::Invalid;
diff --git a/src/app/ReadPrepareParams.h b/src/app/ReadPrepareParams.h
index 5fc760d..45caaa5 100644
--- a/src/app/ReadPrepareParams.h
+++ b/src/app/ReadPrepareParams.h
@@ -19,6 +19,7 @@
#pragma once
#include <app/AttributePathParams.h>
+#include <app/DataVersionFilter.h>
#include <app/EventPathParams.h>
#include <app/InteractionModelTimeout.h>
#include <app/util/basic-types.h>
@@ -45,6 +46,8 @@
size_t mEventPathParamsListSize = 0;
AttributePathParams * mpAttributePathParamsList = nullptr;
size_t mAttributePathParamsListSize = 0;
+ DataVersionFilter * mpDataVersionFilterList = nullptr;
+ size_t mDataVersionFilterListSize = 0;
EventNumber mEventNumber = 0;
System::Clock::Timeout mTimeout = kImMessageTimeout;
uint16_t mMinIntervalFloorSeconds = 0;
@@ -62,6 +65,8 @@
mEventPathParamsListSize = other.mEventPathParamsListSize;
mpAttributePathParamsList = other.mpAttributePathParamsList;
mAttributePathParamsListSize = other.mAttributePathParamsListSize;
+ mpDataVersionFilterList = other.mpDataVersionFilterList;
+ mDataVersionFilterListSize = other.mDataVersionFilterListSize;
mEventNumber = other.mEventNumber;
mMinIntervalFloorSeconds = other.mMinIntervalFloorSeconds;
mMaxIntervalCeilingSeconds = other.mMaxIntervalCeilingSeconds;
@@ -85,6 +90,8 @@
mEventPathParamsListSize = other.mEventPathParamsListSize;
mpAttributePathParamsList = other.mpAttributePathParamsList;
mAttributePathParamsListSize = other.mAttributePathParamsListSize;
+ mpDataVersionFilterList = other.mpDataVersionFilterList;
+ mDataVersionFilterListSize = other.mDataVersionFilterListSize;
mEventNumber = other.mEventNumber;
mMinIntervalFloorSeconds = other.mMinIntervalFloorSeconds;
mMaxIntervalCeilingSeconds = other.mMaxIntervalCeilingSeconds;
diff --git a/src/app/reporting/Engine.cpp b/src/app/reporting/Engine.cpp
index 244e896..5cdd5a8 100644
--- a/src/app/reporting/Engine.cpp
+++ b/src/app/reporting/Engine.cpp
@@ -47,6 +47,24 @@
mGlobalDirtySet.ReleaseAll();
}
+bool Engine::IsClusterDataVersionMatch(ClusterInfo * aDataVersionFilterList, const ConcreteReadAttributePath & aPath)
+{
+ bool existPathMatch = false;
+ bool existVersionMismatch = false;
+ for (auto filter = aDataVersionFilterList; filter != nullptr; filter = filter->mpNext)
+ {
+ if (aPath.mEndpointId == filter->mEndpointId && aPath.mClusterId == filter->mClusterId)
+ {
+ existPathMatch = true;
+ if (!IsClusterDataVersionEqual(filter->mEndpointId, filter->mClusterId, filter->mDataVersion.Value()))
+ {
+ existVersionMismatch = true;
+ }
+ }
+ }
+ return existPathMatch && !existVersionMismatch;
+}
+
CHIP_ERROR
Engine::RetrieveClusterData(const SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered,
AttributeReportIBs::Builder & aAttributeReportIBs, const ConcreteReadAttributePath & aPath,
@@ -111,6 +129,13 @@
continue;
}
}
+ else
+ {
+ if (IsClusterDataVersionMatch(apReadHandler->GetDataVersionFilterlist(), readPath))
+ {
+ continue;
+ }
+ }
// If we are processing a read request, or the initial report of a subscription, just regard all paths as dirty paths.
TLV::TLVWriter attributeBackup;
diff --git a/src/app/reporting/Engine.h b/src/app/reporting/Engine.h
index 1fa762b..b8f430b 100644
--- a/src/app/reporting/Engine.h
+++ b/src/app/reporting/Engine.h
@@ -118,6 +118,13 @@
const ConcreteReadAttributePath & aClusterInfo,
AttributeValueEncoder::AttributeEncodeState * apEncoderState);
+ // If version match, it means don't send, if version mismatch, it means send.
+ // If client sends the same path with multiple data versions, client will get the data back per the spec, because at least one
+ // of those will fail to match. This function should return false if either nothing in the list matches the given
+ // endpoint+cluster in the path or there is an entry in the list that matches the endpoint+cluster in the path but does not
+ // match the current data version of that cluster.
+ bool IsClusterDataVersionMatch(ClusterInfo * aDataVersionFilterList, const ConcreteReadAttributePath & aPath);
+
/**
* Check all active subscription, if the subscription has no paths that intersect with global dirty set,
* it would clear dirty flag for that subscription
diff --git a/src/app/tests/TestAttributeCache.cpp b/src/app/tests/TestAttributeCache.cpp
index f5d6048..6df0a7b 100644
--- a/src/app/tests/TestAttributeCache.cpp
+++ b/src/app/tests/TestAttributeCache.cpp
@@ -126,7 +126,6 @@
System::PacketBufferTLVReader reader;
ReadClient::Callback * callback = mReadCallback;
StatusIB status;
- DataVersion version = kUndefinedDataVersion;
callback->OnReportBegin();
uint8_t index = 0;
@@ -198,13 +197,13 @@
writer.Finalize(&handle);
reader.Init(std::move(handle));
NL_TEST_ASSERT(gSuite, reader.Next() == CHIP_NO_ERROR);
- callback->OnAttributeData(path, version, &reader, status);
+ callback->OnAttributeData(path, &reader, status);
}
else
{
ChipLogProgress(DataManagement, "\t -- Generating Status");
status.mStatus = Protocols::InteractionModel::Status::Failure;
- callback->OnAttributeData(path, version, nullptr, status);
+ callback->OnAttributeData(path, nullptr, status);
}
index++;
diff --git a/src/app/tests/TestBufferedReadCallback.cpp b/src/app/tests/TestBufferedReadCallback.cpp
index d53e36f..82f45c6 100644
--- a/src/app/tests/TestBufferedReadCallback.cpp
+++ b/src/app/tests/TestBufferedReadCallback.cpp
@@ -80,8 +80,7 @@
void OnReportBegin() override;
void OnReportEnd() override;
- void OnAttributeData(const ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
- const StatusIB & aStatus) override;
+ void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) override;
void OnDone() override {}
std::vector<ValidationInstruction> mInstructionList;
@@ -95,7 +94,7 @@
void DataSeriesValidator::OnReportEnd() {}
-void DataSeriesValidator::OnAttributeData(const ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
+void DataSeriesValidator::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
const StatusIB & aStatus)
{
uint32_t expectedListLength;
@@ -302,7 +301,6 @@
ReadClient::Callback * callback = &mReadCallback;
StatusIB status;
bool hasData;
- DataVersion version = kUndefinedDataVersion;
callback->OnReportBegin();
@@ -403,7 +401,7 @@
path.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
status.mStatus = Protocols::InteractionModel::Status::Failure;
hasData = false;
- callback->OnAttributeData(path, version, &reader, status);
+ callback->OnAttributeData(path, &reader, status);
break;
}
@@ -414,7 +412,7 @@
path.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
status.mStatus = Protocols::InteractionModel::Status::Failure;
hasData = false;
- callback->OnAttributeData(path, version, &reader, status);
+ callback->OnAttributeData(path, &reader, status);
break;
}
@@ -432,7 +430,7 @@
writer.Finalize(&handle);
reader.Init(std::move(handle));
NL_TEST_ASSERT(gSuite, reader.Next() == CHIP_NO_ERROR);
- callback->OnAttributeData(path, version, &reader, status);
+ callback->OnAttributeData(path, &reader, status);
}
ChipLogProgress(DataManagement, "\t -- Generating C0..C512");
@@ -455,7 +453,7 @@
writer.Finalize(&handle);
reader.Init(std::move(handle));
NL_TEST_ASSERT(gSuite, reader.Next() == CHIP_NO_ERROR);
- callback->OnAttributeData(path, version, &reader, status);
+ callback->OnAttributeData(path, &reader, status);
}
break;
@@ -475,7 +473,7 @@
writer.Finalize(&handle);
reader.Init(std::move(handle));
NL_TEST_ASSERT(gSuite, reader.Next() == CHIP_NO_ERROR);
- callback->OnAttributeData(path, version, &reader, status);
+ callback->OnAttributeData(path, &reader, status);
}
ChipLogProgress(DataManagement, "\t -- Generating D0..D512");
@@ -494,7 +492,7 @@
writer.Finalize(&handle);
reader.Init(std::move(handle));
NL_TEST_ASSERT(gSuite, reader.Next() == CHIP_NO_ERROR);
- callback->OnAttributeData(path, version, &reader, status);
+ callback->OnAttributeData(path, &reader, status);
}
break;
@@ -509,7 +507,7 @@
writer.Finalize(&handle);
reader.Init(std::move(handle));
NL_TEST_ASSERT(gSuite, reader.Next() == CHIP_NO_ERROR);
- callback->OnAttributeData(path, version, &reader, status);
+ callback->OnAttributeData(path, &reader, status);
}
index++;
diff --git a/src/app/tests/TestMessageDef.cpp b/src/app/tests/TestMessageDef.cpp
index 37dead6..95c3cd5 100644
--- a/src/app/tests/TestMessageDef.cpp
+++ b/src/app/tests/TestMessageDef.cpp
@@ -652,7 +652,7 @@
{
CHIP_ERROR err = CHIP_NO_ERROR;
AttributePathIB::Parser attributePathParser;
- chip::DataVersion version = chip::kUndefinedDataVersion;
+ chip::DataVersion version = 0;
#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK
err = aAttributeDataIBParser.CheckSchemaValidity();
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp
index 941f180..f5d4130 100644
--- a/src/app/tests/TestReadInteraction.cpp
+++ b/src/app/tests/TestReadInteraction.cpp
@@ -49,13 +49,16 @@
uint8_t gInfoEventBuffer[128];
uint8_t gCritEventBuffer[128];
chip::app::CircularEventBuffer gCircularEventBuffer[3];
-chip::ClusterId kTestClusterId = 6;
-chip::ClusterId kInvalidTestClusterId = 7;
-chip::EndpointId kTestEndpointId = 1;
-chip::EventId kTestEventIdDebug = 1;
-chip::EventId kTestEventIdCritical = 2;
-uint8_t kTestFieldValue1 = 1;
-chip::TLV::Tag kTestEventTag = chip::TLV::ContextTag(1);
+chip::ClusterId kTestClusterId = 6;
+chip::ClusterId kInvalidTestClusterId = 7;
+chip::EndpointId kTestEndpointId = 1;
+chip::EventId kTestEventIdDebug = 1;
+chip::EventId kTestEventIdCritical = 2;
+uint8_t kTestFieldValue1 = 1;
+chip::TLV::Tag kTestEventTag = chip::TLV::ContextTag(1);
+chip::EndpointId kInvalidTestEndpointId = 3;
+chip::DataVersion kTestDataVersion1 = 3;
+chip::DataVersion kTestDataVersion2 = 5;
class TestContext : public chip::Test::AppContext
{
@@ -143,8 +146,8 @@
mGotEventResponse = true;
}
- void OnAttributeData(const chip::app::ConcreteDataAttributePath & aPath, chip::DataVersion aVersion,
- chip::TLV::TLVReader * apData, const chip::app::StatusIB & status) override
+ void OnAttributeData(const chip::app::ConcreteDataAttributePath & aPath, chip::TLV::TLVReader * apData,
+ const chip::app::StatusIB & status) override
{
if (status.mStatus == chip::Protocols::InteractionModel::Status::Success)
{
@@ -168,6 +171,11 @@
{
delete[] aReadPrepareParams.mpEventPathParamsList;
}
+
+ if (aReadPrepareParams.mpDataVersionFilterList != nullptr)
+ {
+ delete[] aReadPrepareParams.mpDataVersionFilterList;
+ }
}
int mNumDataElementIndex = 0;
@@ -230,6 +238,18 @@
return AttributeValueEncoder(aAttributeReports, 0, aPath, 0).Encode(kTestFieldValue1);
}
+bool IsClusterDataVersionEqual(EndpointId aEndpointId, ClusterId aClusterId, DataVersion aRequiredVersion)
+{
+ if (kTestDataVersion1 == aRequiredVersion)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
class TestReadInteraction
{
public:
@@ -243,6 +263,10 @@
static void TestReadHandlerInvalidAttributePath(nlTestSuite * apSuite, void * apContext);
static void TestProcessSubscribeRequest(nlTestSuite * apSuite, void * apContext);
static void TestReadRoundtrip(nlTestSuite * apSuite, void * apContext);
+ static void TestReadRoundtripWithDataVersionFilter(nlTestSuite * apSuite, void * apContext);
+ static void TestReadRoundtripWithNoMatchPathDataVersionFilter(nlTestSuite * apSuite, void * apContext);
+ static void TestReadRoundtripWithMultiSamePathDifferentDataVersionFilter(nlTestSuite * apSuite, void * apContext);
+ static void TestReadRoundtripWithSameDifferentPathsDataVersionFilter(nlTestSuite * apSuite, void * apContext);
static void TestReadWildcard(nlTestSuite * apSuite, void * apContext);
static void TestReadChunking(nlTestSuite * apSuite, void * apContext);
static void TestSetDirtyBetweenChunks(nlTestSuite * apSuite, void * apContext);
@@ -728,6 +752,244 @@
NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
}
+void TestReadInteraction::TestReadRoundtripWithDataVersionFilter(nlTestSuite * apSuite, void * apContext)
+{
+ TestContext & ctx = *static_cast<TestContext *>(apContext);
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr();
+ // Shouldn't have anything in the retransmit table when starting the test.
+ NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0);
+
+ GenerateEvents(apSuite, apContext);
+
+ MockInteractionModelApp delegate;
+ auto * engine = chip::app::InteractionModelEngine::GetInstance();
+ err = engine->Init(&ctx.GetExchangeManager());
+ NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+ NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse);
+
+ chip::app::AttributePathParams attributePathParams[2];
+ attributePathParams[0].mEndpointId = kTestEndpointId;
+ attributePathParams[0].mClusterId = kTestClusterId;
+ attributePathParams[0].mAttributeId = 1;
+
+ attributePathParams[1].mEndpointId = kTestEndpointId;
+ attributePathParams[1].mClusterId = kTestClusterId;
+ attributePathParams[1].mAttributeId = 2;
+ attributePathParams[1].mListIndex = 1;
+
+ chip::app::DataVersionFilter dataVersionFilters[1];
+ dataVersionFilters[0].mEndpointId = kTestEndpointId;
+ dataVersionFilters[0].mClusterId = kTestClusterId;
+ dataVersionFilters[0].mDataVersion.SetValue(kTestDataVersion1);
+
+ ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice());
+ readPrepareParams.mpAttributePathParamsList = attributePathParams;
+ readPrepareParams.mAttributePathParamsListSize = 2;
+ readPrepareParams.mpDataVersionFilterList = dataVersionFilters;
+ readPrepareParams.mDataVersionFilterListSize = 1;
+
+ {
+ app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate,
+ chip::app::ReadClient::InteractionType::Read);
+
+ err = readClient.SendRequest(readPrepareParams);
+ NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+
+ InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ NL_TEST_ASSERT(apSuite, delegate.mNumAttributeResponse == 0);
+
+ delegate.mNumAttributeResponse = 0;
+ }
+
+ NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadClients() == 0);
+ engine->Shutdown();
+ NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
+}
+
+void TestReadInteraction::TestReadRoundtripWithNoMatchPathDataVersionFilter(nlTestSuite * apSuite, void * apContext)
+{
+ TestContext & ctx = *static_cast<TestContext *>(apContext);
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr();
+ // Shouldn't have anything in the retransmit table when starting the test.
+ NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0);
+
+ GenerateEvents(apSuite, apContext);
+
+ MockInteractionModelApp delegate;
+ auto * engine = chip::app::InteractionModelEngine::GetInstance();
+ err = engine->Init(&ctx.GetExchangeManager());
+ NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+
+ chip::app::AttributePathParams attributePathParams[2];
+ attributePathParams[0].mEndpointId = kTestEndpointId;
+ attributePathParams[0].mClusterId = kTestClusterId;
+ attributePathParams[0].mAttributeId = 1;
+
+ attributePathParams[1].mEndpointId = kTestEndpointId;
+ attributePathParams[1].mClusterId = kTestClusterId;
+ attributePathParams[1].mAttributeId = 2;
+ attributePathParams[1].mListIndex = 1;
+
+ chip::app::DataVersionFilter dataVersionFilters[2];
+ dataVersionFilters[0].mEndpointId = kTestEndpointId;
+ dataVersionFilters[0].mClusterId = kInvalidTestClusterId;
+ dataVersionFilters[0].mDataVersion.SetValue(kTestDataVersion1);
+
+ dataVersionFilters[1].mEndpointId = kInvalidTestEndpointId;
+ dataVersionFilters[1].mClusterId = kTestClusterId;
+ dataVersionFilters[1].mDataVersion.SetValue(kTestDataVersion2);
+
+ ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice());
+ readPrepareParams.mpAttributePathParamsList = attributePathParams;
+ readPrepareParams.mAttributePathParamsListSize = 2;
+ readPrepareParams.mpDataVersionFilterList = dataVersionFilters;
+ readPrepareParams.mDataVersionFilterListSize = 2;
+
+ {
+ app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate,
+ chip::app::ReadClient::InteractionType::Read);
+
+ err = readClient.SendRequest(readPrepareParams);
+ NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+
+ InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ NL_TEST_ASSERT(apSuite, delegate.mNumAttributeResponse == 2);
+ NL_TEST_ASSERT(apSuite, !delegate.mReadError);
+
+ delegate.mNumAttributeResponse = 0;
+ }
+
+ NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadClients() == 0);
+ engine->Shutdown();
+ NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
+}
+
+void TestReadInteraction::TestReadRoundtripWithMultiSamePathDifferentDataVersionFilter(nlTestSuite * apSuite, void * apContext)
+{
+ TestContext & ctx = *static_cast<TestContext *>(apContext);
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr();
+ // Shouldn't have anything in the retransmit table when starting the test.
+ NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0);
+
+ GenerateEvents(apSuite, apContext);
+
+ MockInteractionModelApp delegate;
+ auto * engine = chip::app::InteractionModelEngine::GetInstance();
+ err = engine->Init(&ctx.GetExchangeManager());
+ NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+ NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse);
+
+ chip::app::AttributePathParams attributePathParams[2];
+ attributePathParams[0].mEndpointId = kTestEndpointId;
+ attributePathParams[0].mClusterId = kTestClusterId;
+ attributePathParams[0].mAttributeId = 1;
+
+ attributePathParams[1].mEndpointId = kTestEndpointId;
+ attributePathParams[1].mClusterId = kTestClusterId;
+ attributePathParams[1].mAttributeId = 2;
+ attributePathParams[1].mListIndex = 1;
+
+ chip::app::DataVersionFilter dataVersionFilters[2];
+ dataVersionFilters[0].mEndpointId = kTestEndpointId;
+ dataVersionFilters[0].mClusterId = kTestClusterId;
+ dataVersionFilters[0].mDataVersion.SetValue(kTestDataVersion1);
+
+ dataVersionFilters[1].mEndpointId = kTestEndpointId;
+ dataVersionFilters[1].mClusterId = kTestClusterId;
+ dataVersionFilters[1].mDataVersion.SetValue(kTestDataVersion2);
+
+ ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice());
+ readPrepareParams.mpAttributePathParamsList = attributePathParams;
+ readPrepareParams.mAttributePathParamsListSize = 2;
+ readPrepareParams.mpDataVersionFilterList = dataVersionFilters;
+ readPrepareParams.mDataVersionFilterListSize = 2;
+
+ {
+ app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate,
+ chip::app::ReadClient::InteractionType::Read);
+
+ err = readClient.SendRequest(readPrepareParams);
+ NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+
+ InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ NL_TEST_ASSERT(apSuite, delegate.mNumAttributeResponse == 2);
+ NL_TEST_ASSERT(apSuite, !delegate.mReadError);
+
+ delegate.mNumAttributeResponse = 0;
+ }
+
+ NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadClients() == 0);
+ engine->Shutdown();
+ NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
+}
+
+void TestReadInteraction::TestReadRoundtripWithSameDifferentPathsDataVersionFilter(nlTestSuite * apSuite, void * apContext)
+{
+ TestContext & ctx = *static_cast<TestContext *>(apContext);
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr();
+ // Shouldn't have anything in the retransmit table when starting the test.
+ NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0);
+
+ GenerateEvents(apSuite, apContext);
+
+ MockInteractionModelApp delegate;
+ auto * engine = chip::app::InteractionModelEngine::GetInstance();
+ err = engine->Init(&ctx.GetExchangeManager());
+ NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+ NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse);
+
+ chip::app::AttributePathParams attributePathParams[2];
+ attributePathParams[0].mEndpointId = kTestEndpointId;
+ attributePathParams[0].mClusterId = kTestClusterId;
+ attributePathParams[0].mAttributeId = 1;
+
+ attributePathParams[1].mEndpointId = kTestEndpointId;
+ attributePathParams[1].mClusterId = kTestClusterId;
+ attributePathParams[1].mAttributeId = 2;
+ attributePathParams[1].mListIndex = 1;
+
+ chip::app::DataVersionFilter dataVersionFilters[2];
+ dataVersionFilters[0].mEndpointId = kTestEndpointId;
+ dataVersionFilters[0].mClusterId = kTestClusterId;
+ dataVersionFilters[0].mDataVersion.SetValue(kTestDataVersion1);
+
+ dataVersionFilters[1].mEndpointId = kInvalidTestEndpointId;
+ dataVersionFilters[1].mClusterId = kTestClusterId;
+ dataVersionFilters[1].mDataVersion.SetValue(kTestDataVersion2);
+
+ ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice());
+ readPrepareParams.mpAttributePathParamsList = attributePathParams;
+ readPrepareParams.mAttributePathParamsListSize = 2;
+ readPrepareParams.mpDataVersionFilterList = dataVersionFilters;
+ readPrepareParams.mDataVersionFilterListSize = 2;
+
+ {
+ app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate,
+ chip::app::ReadClient::InteractionType::Read);
+
+ err = readClient.SendRequest(readPrepareParams);
+ NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+
+ InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ NL_TEST_ASSERT(apSuite, delegate.mNumAttributeResponse == 0);
+ NL_TEST_ASSERT(apSuite, !delegate.mReadError);
+
+ delegate.mNumAttributeResponse = 0;
+ }
+
+ NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadClients() == 0);
+ engine->Shutdown();
+ NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
+}
+
void TestReadInteraction::TestReadWildcard(nlTestSuite * apSuite, void * apContext)
{
TestContext & ctx = *static_cast<TestContext *>(apContext);
@@ -1497,6 +1759,10 @@
const nlTest sTests[] =
{
NL_TEST_DEF("TestReadRoundtrip", chip::app::TestReadInteraction::TestReadRoundtrip),
+ NL_TEST_DEF("TestReadRoundtripWithDataVersionFilter", chip::app::TestReadInteraction::TestReadRoundtripWithDataVersionFilter),
+ NL_TEST_DEF("TestReadRoundtripWithNoMatchPathDataVersionFilter", chip::app::TestReadInteraction::TestReadRoundtripWithNoMatchPathDataVersionFilter),
+ NL_TEST_DEF("TestReadRoundtripWithMultiSamePathDifferentDataVersionFilter", chip::app::TestReadInteraction::TestReadRoundtripWithMultiSamePathDifferentDataVersionFilter),
+ NL_TEST_DEF("TestReadRoundtripWithSameDifferentPathsDataVersionFilter", chip::app::TestReadInteraction::TestReadRoundtripWithSameDifferentPathsDataVersionFilter),
NL_TEST_DEF("TestReadWildcard", chip::app::TestReadInteraction::TestReadWildcard),
NL_TEST_DEF("TestReadChunking", chip::app::TestReadInteraction::TestReadChunking),
NL_TEST_DEF("TestSetDirtyBetweenChunks", chip::app::TestReadInteraction::TestSetDirtyBetweenChunks),
diff --git a/src/app/tests/integration/chip_im_initiator.cpp b/src/app/tests/integration/chip_im_initiator.cpp
index 56c0cad..6164700 100644
--- a/src/app/tests/integration/chip_im_initiator.cpp
+++ b/src/app/tests/integration/chip_im_initiator.cpp
@@ -145,8 +145,8 @@
}
}
}
- void OnAttributeData(const chip::app::ConcreteDataAttributePath & aPath, chip::DataVersion aVersion,
- chip::TLV::TLVReader * aData, const chip::app::StatusIB & status) override
+ void OnAttributeData(const chip::app::ConcreteDataAttributePath & aPath, chip::TLV::TLVReader * aData,
+ const chip::app::StatusIB & status) override
{}
void OnError(CHIP_ERROR aError) override { printf("ReadError with err %" CHIP_ERROR_FORMAT, aError.Format()); }
@@ -667,6 +667,11 @@
}
return CHIP_NO_ERROR;
}
+
+bool IsClusterDataVersionEqual(EndpointId aEndpointId, ClusterId aClusterId, DataVersion aRequiredVersion)
+{
+ return true;
+}
} // namespace app
} // namespace chip
diff --git a/src/app/tests/integration/chip_im_responder.cpp b/src/app/tests/integration/chip_im_responder.cpp
index 481dbae..2dbce00 100644
--- a/src/app/tests/integration/chip_im_responder.cpp
+++ b/src/app/tests/integration/chip_im_responder.cpp
@@ -129,6 +129,11 @@
err = apWriteHandler->AddStatus(attributePath, Protocols::InteractionModel::Status::Success);
return err;
}
+
+bool IsClusterDataVersionEqual(EndpointId aEndpointId, ClusterId aClusterId, DataVersion aRequiredVersion)
+{
+ return true;
+}
} // namespace app
} // namespace chip
diff --git a/src/app/util/ember-compatibility-functions.cpp b/src/app/util/ember-compatibility-functions.cpp
index f7bc0ab..a62e03c 100644
--- a/src/app/util/ember-compatibility-functions.cpp
+++ b/src/app/util/ember-compatibility-functions.cpp
@@ -403,7 +403,7 @@
{
AttributeValueEncoder::AttributeEncodeState state =
(aEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *aEncoderState);
- DataVersion version = kUndefinedDataVersion;
+ DataVersion version = 0;
ReturnErrorOnFailure(ReadClusterDataVersion(aPath.mEndpointId, aPath.mClusterId, version));
AttributeValueEncoder valueEncoder(aAttributeReports, aAccessingFabricIndex, aPath, version, aIsFabricFiltered, state);
CHIP_ERROR err = aAccessInterface->Read(aPath, valueEncoder);
@@ -536,7 +536,7 @@
AttributeDataIB::Builder & attributeDataIBBuilder = attributeReport.CreateAttributeData();
ReturnErrorOnFailure(attributeDataIBBuilder.GetError());
- DataVersion version = kUndefinedDataVersion;
+ DataVersion version = 0;
ReturnErrorOnFailure(ReadClusterDataVersion(aPath.mEndpointId, aPath.mClusterId, version));
attributeDataIBBuilder.DataVersion(version);
ReturnErrorOnFailure(attributeDataIBBuilder.GetError());
@@ -980,6 +980,21 @@
return apWriteHandler->AddStatus(aPath, status);
}
+bool IsClusterDataVersionEqual(EndpointId aEndpointId, ClusterId aClusterId, DataVersion aRequiredVersion)
+{
+ DataVersion * version = emberAfDataVersionStorage(aEndpointId, aClusterId);
+ if (version == nullptr)
+ {
+ ChipLogError(DataManagement, "Endpoint %" PRIx16 ", Cluster " ChipLogFormatMEI " not found in IsClusterDataVersionEqual!",
+ aEndpointId, ChipLogValueMEI(aClusterId));
+ return false;
+ }
+ else
+ {
+ return (*(version)) == aRequiredVersion;
+ }
+}
+
} // namespace app
} // namespace chip
diff --git a/src/controller/CHIPCluster.h b/src/controller/CHIPCluster.h
index 47ab6ce..942f287 100644
--- a/src/controller/CHIPCluster.h
+++ b/src/controller/CHIPCluster.h
@@ -187,15 +187,16 @@
*/
template <typename AttributeInfo>
CHIP_ERROR ReadAttribute(void * context, ReadResponseSuccessCallback<typename AttributeInfo::DecodableArgType> successCb,
- ReadResponseFailureCallback failureCb)
+ ReadResponseFailureCallback failureCb, const Optional<DataVersion> & aDataVersion = NullOptional)
{
return ReadAttribute<typename AttributeInfo::DecodableType, typename AttributeInfo::DecodableArgType>(
- context, AttributeInfo::GetClusterId(), AttributeInfo::GetAttributeId(), successCb, failureCb);
+ context, AttributeInfo::GetClusterId(), AttributeInfo::GetAttributeId(), successCb, failureCb, aDataVersion);
}
template <typename DecodableType, typename DecodableArgType>
CHIP_ERROR ReadAttribute(void * context, ClusterId clusterId, AttributeId attributeId,
- ReadResponseSuccessCallback<DecodableArgType> successCb, ReadResponseFailureCallback failureCb)
+ ReadResponseSuccessCallback<DecodableArgType> successCb, ReadResponseFailureCallback failureCb,
+ const Optional<DataVersion> & aDataVersion = NullOptional)
{
VerifyOrReturnError(mDevice != nullptr, CHIP_ERROR_INCORRECT_STATE);
@@ -214,7 +215,8 @@
};
return Controller::ReadAttribute<DecodableType>(mDevice->GetExchangeManager(), mDevice->GetSecureSession().Value(),
- mEndpoint, clusterId, attributeId, onSuccessCb, onFailureCb);
+ mEndpoint, clusterId, attributeId, onSuccessCb, onFailureCb, true,
+ aDataVersion);
}
/**
@@ -225,18 +227,20 @@
CHIP_ERROR SubscribeAttribute(void * context, ReadResponseSuccessCallback<typename AttributeInfo::DecodableArgType> reportCb,
ReadResponseFailureCallback failureCb, uint16_t minIntervalFloorSeconds,
uint16_t maxIntervalCeilingSeconds,
- SubscriptionEstablishedCallback subscriptionEstablishedCb = nullptr)
+ SubscriptionEstablishedCallback subscriptionEstablishedCb = nullptr,
+ const Optional<DataVersion> & aDataVersion = NullOptional)
{
return SubscribeAttribute<typename AttributeInfo::DecodableType, typename AttributeInfo::DecodableArgType>(
context, AttributeInfo::GetClusterId(), AttributeInfo::GetAttributeId(), reportCb, failureCb, minIntervalFloorSeconds,
- maxIntervalCeilingSeconds, subscriptionEstablishedCb);
+ maxIntervalCeilingSeconds, subscriptionEstablishedCb, aDataVersion);
}
template <typename DecodableType, typename DecodableArgType>
CHIP_ERROR SubscribeAttribute(void * context, ClusterId clusterId, AttributeId attributeId,
ReadResponseSuccessCallback<DecodableArgType> reportCb, ReadResponseFailureCallback failureCb,
uint16_t minIntervalFloorSeconds, uint16_t maxIntervalCeilingSeconds,
- SubscriptionEstablishedCallback subscriptionEstablishedCb = nullptr)
+ SubscriptionEstablishedCallback subscriptionEstablishedCb = nullptr,
+ const Optional<DataVersion> & aDataVersion = NullOptional)
{
VerifyOrReturnError(mDevice != nullptr, CHIP_ERROR_INCORRECT_STATE);
@@ -261,9 +265,10 @@
}
};
- return Controller::SubscribeAttribute<DecodableType>(
- mDevice->GetExchangeManager(), mDevice->GetSecureSession().Value(), mEndpoint, clusterId, attributeId, onReportCb,
- onFailureCb, minIntervalFloorSeconds, maxIntervalCeilingSeconds, onSubscriptionEstablishedCb);
+ return Controller::SubscribeAttribute<DecodableType>(mDevice->GetExchangeManager(), mDevice->GetSecureSession().Value(),
+ mEndpoint, clusterId, attributeId, onReportCb, onFailureCb,
+ minIntervalFloorSeconds, maxIntervalCeilingSeconds,
+ onSubscriptionEstablishedCb, true, false, aDataVersion);
}
/**
diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp
index 3242226..47add2e 100644
--- a/src/controller/CHIPDeviceController.cpp
+++ b/src/controller/CHIPDeviceController.cpp
@@ -483,7 +483,6 @@
constexpr EndpointId kBasicClusterEndpoint = 0;
chip::Controller::BasicCluster cluster;
cluster.Associate(device, kBasicClusterEndpoint);
-
return cluster.ReadAttribute<app::Clusters::Basic::Attributes::VendorID::TypeInfo>(this, OnVIDReadResponse,
OnVIDPIDReadFailureResponse);
}
diff --git a/src/controller/ReadInteraction.h b/src/controller/ReadInteraction.h
index 57101cf..9296f02 100644
--- a/src/controller/ReadInteraction.h
+++ b/src/controller/ReadInteraction.h
@@ -43,7 +43,8 @@
template <typename DecodableAttributeType>
CHIP_ERROR ReportAttribute(Messaging::ExchangeManager * exchangeMgr, EndpointId endpointId, ClusterId clusterId,
- AttributeId attributeId, ReportAttributeParams<DecodableAttributeType> && readParams)
+ AttributeId attributeId, ReportAttributeParams<DecodableAttributeType> && readParams,
+ const Optional<DataVersion> & aDataVersion = NullOptional)
{
app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
CHIP_ERROR err = CHIP_NO_ERROR;
@@ -52,7 +53,14 @@
VerifyOrReturnError(readPaths != nullptr, CHIP_ERROR_NO_MEMORY);
readParams.mpAttributePathParamsList = readPaths.get();
readParams.mAttributePathParamsListSize = 1;
-
+ chip::Platform::UniquePtr<chip::app::DataVersionFilter> dataVersionFilters;
+ if (aDataVersion.HasValue())
+ {
+ dataVersionFilters = Platform::MakeUnique<app::DataVersionFilter>(endpointId, clusterId, aDataVersion.Value());
+ VerifyOrReturnError(dataVersionFilters != nullptr, CHIP_ERROR_NO_MEMORY);
+ readParams.mpDataVersionFilterList = dataVersionFilters.get();
+ readParams.mDataVersionFilterListSize = 1;
+ }
auto onDone = [](TypedReadAttributeCallback<DecodableAttributeType> * callback) { chip::Platform::Delete(callback); };
auto callback = chip::Platform::MakeUnique<TypedReadAttributeCallback<DecodableAttributeType>>(
@@ -66,6 +74,8 @@
if (readClient->IsSubscriptionType())
{
readPaths.release();
+ dataVersionFilters.release();
+
err = readClient->SendAutoResubscribeRequest(std::move(readParams));
ReturnErrorOnFailure(err);
}
@@ -97,13 +107,13 @@
ClusterId clusterId, AttributeId attributeId,
typename TypedReadAttributeCallback<DecodableAttributeType>::OnSuccessCallbackType onSuccessCb,
typename TypedReadAttributeCallback<DecodableAttributeType>::OnErrorCallbackType onErrorCb,
- bool fabricFiltered = true)
+ bool fabricFiltered = true, const Optional<DataVersion> & aDataVersion = NullOptional)
{
detail::ReportAttributeParams<DecodableAttributeType> params(sessionHandle);
params.mOnReportCb = onSuccessCb;
params.mOnErrorCb = onErrorCb;
params.mIsFabricFiltered = fabricFiltered;
- return detail::ReportAttribute(exchangeMgr, endpointId, clusterId, attributeId, std::move(params));
+ return detail::ReportAttribute(exchangeMgr, endpointId, clusterId, attributeId, std::move(params), aDataVersion);
}
/*
@@ -120,23 +130,23 @@
ReadAttribute(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId,
typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnSuccessCallbackType onSuccessCb,
typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnErrorCallbackType onErrorCb,
- bool fabricFiltered = true)
+ bool fabricFiltered = true, const Optional<DataVersion> & aDataVersion = NullOptional)
{
return ReadAttribute<typename AttributeTypeInfo::DecodableType>(
exchangeMgr, sessionHandle, endpointId, AttributeTypeInfo::GetClusterId(), AttributeTypeInfo::GetAttributeId(), onSuccessCb,
- onErrorCb, fabricFiltered);
+ onErrorCb, fabricFiltered, aDataVersion);
}
// Helper for SubscribeAttribute to reduce the amount of code generated.
template <typename DecodableAttributeType>
-CHIP_ERROR SubscribeAttribute(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId,
- ClusterId clusterId, AttributeId attributeId,
- typename TypedReadAttributeCallback<DecodableAttributeType>::OnSuccessCallbackType onReportCb,
- typename TypedReadAttributeCallback<DecodableAttributeType>::OnErrorCallbackType onErrorCb,
- uint16_t minIntervalFloorSeconds, uint16_t maxIntervalCeilingSeconds,
- typename TypedReadAttributeCallback<DecodableAttributeType>::OnSubscriptionEstablishedCallbackType
- onSubscriptionEstablishedCb = nullptr,
- bool fabricFiltered = true, bool keepPreviousSubscriptions = false)
+CHIP_ERROR SubscribeAttribute(
+ Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, ClusterId clusterId,
+ AttributeId attributeId, typename TypedReadAttributeCallback<DecodableAttributeType>::OnSuccessCallbackType onReportCb,
+ typename TypedReadAttributeCallback<DecodableAttributeType>::OnErrorCallbackType onErrorCb, uint16_t minIntervalFloorSeconds,
+ uint16_t maxIntervalCeilingSeconds,
+ typename TypedReadAttributeCallback<DecodableAttributeType>::OnSubscriptionEstablishedCallbackType onSubscriptionEstablishedCb =
+ nullptr,
+ bool fabricFiltered = true, bool keepPreviousSubscriptions = false, const Optional<DataVersion> & aDataVersion = NullOptional)
{
detail::ReportAttributeParams<DecodableAttributeType> params(sessionHandle);
params.mOnReportCb = onReportCb;
@@ -147,7 +157,7 @@
params.mKeepSubscriptions = keepPreviousSubscriptions;
params.mReportType = app::ReadClient::InteractionType::Subscribe;
params.mIsFabricFiltered = fabricFiltered;
- return detail::ReportAttribute(exchangeMgr, endpointId, clusterId, attributeId, std::move(params));
+ return detail::ReportAttribute(exchangeMgr, endpointId, clusterId, attributeId, std::move(params), aDataVersion);
}
/*
@@ -163,12 +173,12 @@
uint16_t aMinIntervalFloorSeconds, uint16_t aMaxIntervalCeilingSeconds,
typename TypedReadAttributeCallback<typename AttributeTypeInfo::DecodableType>::OnSubscriptionEstablishedCallbackType
onSubscriptionEstablishedCb = nullptr,
- bool fabricFiltered = true, bool keepPreviousSubscriptions = false)
+ bool fabricFiltered = true, bool keepPreviousSubscriptions = false, const Optional<DataVersion> & aDataVersion = NullOptional)
{
return SubscribeAttribute<typename AttributeTypeInfo::DecodableType>(
exchangeMgr, sessionHandle, endpointId, AttributeTypeInfo::GetClusterId(), AttributeTypeInfo::GetAttributeId(), onReportCb,
onErrorCb, aMinIntervalFloorSeconds, aMaxIntervalCeilingSeconds, onSubscriptionEstablishedCb, fabricFiltered,
- keepPreviousSubscriptions);
+ keepPreviousSubscriptions, aDataVersion);
}
namespace detail {
diff --git a/src/controller/TypedReadCallback.h b/src/controller/TypedReadCallback.h
index a842f6d..12b4200 100644
--- a/src/controller/TypedReadCallback.h
+++ b/src/controller/TypedReadCallback.h
@@ -49,9 +49,9 @@
{
public:
using OnSuccessCallbackType =
- std::function<void(const app::ConcreteAttributePath & aPath, const DecodableAttributeType & aData)>;
- using OnErrorCallbackType = std::function<void(const app::ConcreteAttributePath * aPath, CHIP_ERROR aError)>;
- using OnDoneCallbackType = std::function<void(TypedReadAttributeCallback * callback)>;
+ std::function<void(const app::ConcreteDataAttributePath & aPath, const DecodableAttributeType & aData)>;
+ using OnErrorCallbackType = std::function<void(const app::ConcreteDataAttributePath * aPath, CHIP_ERROR aError)>;
+ using OnDoneCallbackType = std::function<void(TypedReadAttributeCallback * callback)>;
using OnSubscriptionEstablishedCallbackType = std::function<void()>;
TypedReadAttributeCallback(ClusterId aClusterId, AttributeId aAttributeId, OnSuccessCallbackType aOnSuccess,
@@ -67,7 +67,7 @@
void AdoptReadClient(Platform::UniquePtr<app::ReadClient> aReadClient) { mReadClient = std::move(aReadClient); }
private:
- void OnAttributeData(const app::ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
+ void OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
const app::StatusIB & aStatus) override
{
CHIP_ERROR err = CHIP_NO_ERROR;
@@ -108,13 +108,12 @@
void OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) override
{
- if (aReadPrepareParams.mpAttributePathParamsList != nullptr)
- {
- for (size_t i = 0; i < aReadPrepareParams.mAttributePathParamsListSize; i++)
- {
- chip::Platform::Delete<app::AttributePathParams>(&aReadPrepareParams.mpAttributePathParamsList[i]);
- }
- }
+ VerifyOrDie(aReadPrepareParams.mAttributePathParamsListSize == 1 &&
+ aReadPrepareParams.mpAttributePathParamsList != nullptr);
+ chip::Platform::Delete<app::AttributePathParams>(aReadPrepareParams.mpAttributePathParamsList);
+
+ VerifyOrDie(aReadPrepareParams.mDataVersionFilterListSize == 1 && aReadPrepareParams.mpDataVersionFilterList != nullptr);
+ chip::Platform::Delete<app::DataVersionFilter>(aReadPrepareParams.mpDataVersionFilterList);
}
ClusterId mClusterId;
@@ -172,13 +171,8 @@
void OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) override
{
- if (aReadPrepareParams.mpEventPathParamsList != nullptr)
- {
- for (size_t i = 0; i < aReadPrepareParams.mEventPathParamsListSize; i++)
- {
- chip::Platform::Delete<app::EventPathParams>(&aReadPrepareParams.mpEventPathParamsList[i]);
- }
- }
+ VerifyOrDie(aReadPrepareParams.mEventPathParamsListSize == 1 && aReadPrepareParams.mpEventPathParamsList != nullptr);
+ chip::Platform::Delete<app::EventPathParams>(aReadPrepareParams.mpEventPathParamsList);
}
void OnSubscriptionEstablished(uint64_t aSubscriptionId) override
diff --git a/src/controller/java/AndroidCallbacks.cpp b/src/controller/java/AndroidCallbacks.cpp
index cf31fae..cb73e9b 100644
--- a/src/controller/java/AndroidCallbacks.cpp
+++ b/src/controller/java/AndroidCallbacks.cpp
@@ -176,7 +176,7 @@
env->CallVoidMethod(mReportCallbackRef, onReportMethod, map);
}
-void ReportCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
+void ReportCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
const app::StatusIB & aStatus)
{
CHIP_ERROR err = CHIP_NO_ERROR;
diff --git a/src/controller/java/AndroidCallbacks.h b/src/controller/java/AndroidCallbacks.h
index f8ebed8..3935afb 100644
--- a/src/controller/java/AndroidCallbacks.h
+++ b/src/controller/java/AndroidCallbacks.h
@@ -52,7 +52,7 @@
void OnReportEnd() override;
- void OnAttributeData(const app::ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
+ void OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
const app::StatusIB & aStatus) override;
void OnError(CHIP_ERROR aError) override;
diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py
index 517deba..152b76d 100644
--- a/src/controller/python/chip/ChipDeviceCtrl.py
+++ b/src/controller/python/chip/ChipDeviceCtrl.py
@@ -581,7 +581,7 @@
typing.Tuple[int, typing.Type[ClusterObjects.Cluster]],
# Concrete path
typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]]
- ]], returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None, fabricFiltered: bool = True):
+ ]], dataVersionFilters: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]] = None, returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None, fabricFiltered: bool = True):
'''
Read a list of attributes from a target node
@@ -614,6 +614,7 @@
device = self.GetConnectedDeviceSync(nodeid)
attrs = []
+ filters = []
for v in attributes:
endpoint = None
cluster = None
@@ -641,8 +642,24 @@
raise ValueError("Unsupported Attribute Path")
attrs.append(ClusterAttribute.AttributePath(
EndpointId=endpoint, Cluster=cluster, Attribute=attribute))
+ if dataVersionFilters != None:
+ for v in dataVersionFilters:
+ endpoint = None
+ cluster = None
+
+ # endpoint + (cluster) attribute / endpoint + cluster
+ endpoint = v[0]
+ if issubclass(v[1], ClusterObjects.Cluster):
+ cluster = v[1]
+ else:
+ raise ValueError("Unsupported Cluster Path")
+ dataVersion = v[2]
+ filters.append(ClusterAttribute.DataVersionFilter(
+ EndpointId=endpoint, Cluster=cluster, DataVersion=dataVersion))
+ else:
+ filters = None
res = self._ChipStack.Call(
- lambda: ClusterAttribute.ReadAttributes(future, eventLoop, device, self, attrs, returnClusterObject, ClusterAttribute.SubscriptionParameters(reportInterval[0], reportInterval[1]) if reportInterval else None, fabricFiltered=fabricFiltered))
+ lambda: ClusterAttribute.ReadAttributes(future, eventLoop, device, self, attrs, filters, returnClusterObject, ClusterAttribute.SubscriptionParameters(reportInterval[0], reportInterval[1]) if reportInterval else None, fabricFiltered=fabricFiltered))
if res != 0:
raise self._ChipStack.ErrorToException(res)
return await future
@@ -755,7 +772,7 @@
nodeid, [(endpoint, attributeType)]))
path = ClusterAttribute.AttributePath(
EndpointId=endpoint, Attribute=attributeType)
- return im.AttributeReadResult(path=im.AttributePath(nodeId=nodeid, endpointId=path.EndpointId, clusterId=path.ClusterId, attributeId=path.AttributeId), status=0, value=result[endpoint][clusterType][attributeType])
+ return im.AttributeReadResult(path=im.AttributePath(nodeId=nodeid, endpointId=path.EndpointId, clusterId=path.ClusterId, attributeId=path.AttributeId), status=0, value=result[0][endpoint][clusterType][attributeType])
def ZCLWriteAttribute(self, cluster: str, attribute: str, nodeid, endpoint, groupid, value, blocking=True):
req = None
@@ -775,7 +792,7 @@
req = eval(f"GeneratedObjects.{cluster}.Attributes.{attribute}")
except:
raise UnknownAttribute(cluster, attribute)
- return asyncio.run(self.ReadAttribute(nodeid, [(endpoint, req)], False, reportInterval=(minInterval, maxInterval)))
+ return asyncio.run(self.ReadAttribute(nodeid, [(endpoint, req)], None, False, reportInterval=(minInterval, maxInterval)))
def ZCLCommandList(self):
self.CheckIsActive()
diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py
index 9620f1b..f7f0ace 100644
--- a/src/controller/python/chip/clusters/Attribute.py
+++ b/src/controller/python/chip/clusters/Attribute.py
@@ -84,6 +84,31 @@
@dataclass
+class DataVersionFilter:
+ EndpointId: int = None
+ ClusterId: int = None
+ DataVersion: int = None
+
+ def __init__(self, EndpointId: int = None, Cluster=None, ClusterId=None, DataVersion=None):
+ self.EndpointId = EndpointId
+ if Cluster is not None:
+ # Wildcard read for a specific cluster
+ if (ClusterId is not None):
+ raise Warning(
+ "Attribute, ClusterId and AttributeId is ignored when Cluster is specified")
+ self.ClusterId = Cluster.id
+ return
+ self.ClusterId = ClusterId
+ self.DataVersion = DataVersion
+
+ def __str__(self) -> str:
+ return f"{self.EndpointId}/{self.ClusterId}/{self.DataVersion}"
+
+ def __hash__(self):
+ return str(self).__hash__()
+
+
+@dataclass
class TypedAttributePath:
''' Encapsulates an attribute path that has strongly typed references to cluster and attribute
cluster object types. These types serve as keys into the attribute cache.
@@ -317,29 +342,43 @@
default_factory=lambda: {})
attributeCache: Dict[int, List[Cluster]] = field(
default_factory=lambda: {})
+ versionList: Dict[int, Dict[int, Dict[int, int]]] = field(
+ default_factory=lambda: {})
- def UpdateTLV(self, path: AttributePath, dataVersion: int, data: Union[bytes, ValueDecodeFailure]):
+ def UpdateTLV(self, path: AttributePath, dataVersion: int, data: Union[bytes, ValueDecodeFailure]):
''' Store data in TLV since that makes it easiest to eventually convert to either the
cluster or attribute view representations (see below in UpdateCachedData).
'''
if (path.EndpointId not in self.attributeTLVCache):
self.attributeTLVCache[path.EndpointId] = {}
+ if (path.EndpointId not in self.versionList):
+ self.versionList[path.EndpointId] = {}
+
endpointCache = self.attributeTLVCache[path.EndpointId]
+ endpoint = self.versionList[path.EndpointId]
if (path.ClusterId not in endpointCache):
endpointCache[path.ClusterId] = {}
+ if (path.ClusterId not in endpoint):
+ endpoint[path.ClusterId] = {}
+
clusterCache = endpointCache[path.ClusterId]
+ cluster = endpoint[path.ClusterId]
if (path.AttributeId not in clusterCache):
clusterCache[path.AttributeId] = None
+ if (path.AttributeId not in cluster):
+ cluster[path.AttributeId] = None
+
clusterCache[path.AttributeId] = data
+ cluster[path.AttributeId] = dataVersion
def UpdateCachedData(self):
''' This converts the raw TLV data into a cluster object format.
Two formats are available:
- 1. Attribute-View (returnClusterObject=False): Dict[EndpointId, Dict[ClusterObjectType, Dict[AttributeObjectType, AttributeValue]]]
+ 1. Attribute-View (returnClusterObject=False): Dict[EndpointId, Dict[ClusterObjectType, Dict[AttributeObjectType, Dict[AttributeValue, DataVersion]]]]
2. Cluster-View (returnClusterObject=True): Dict[EndpointId, Dict[ClusterObjectType, ClusterValue]]
In the attribute-view, only attributes that match the original path criteria are present in the dictionary. The attribute values can
@@ -639,12 +678,14 @@
if (self._transactionType == TransactionType.READ_EVENTS):
self._future.set_result(self._events)
else:
- self._future.set_result(self._cache.attributeCache)
+ self._future.set_result(
+ (self._cache.attributeCache, self._cache.versionList))
def handleDone(self):
self._event_loop.call_soon_threadsafe(self._handleDone)
def handleReportBegin(self):
+ self._cache.versionList.clear()
pass
def handleReportEnd(self):
@@ -804,11 +845,12 @@
)
-def ReadAttributes(future: Future, eventLoop, device, devCtrl, attributes: List[AttributePath], returnClusterObject: bool = True, subscriptionParameters: SubscriptionParameters = None, fabricFiltered: bool = True) -> int:
+def ReadAttributes(future: Future, eventLoop, device, devCtrl, attributes: List[AttributePath], dataVersionFilters: List[DataVersionFilter] = None, returnClusterObject: bool = True, subscriptionParameters: SubscriptionParameters = None, fabricFiltered: bool = True) -> int:
handle = chip.native.GetLibraryHandle()
transaction = AsyncReadTransaction(
future, eventLoop, devCtrl, TransactionType.READ_ATTRIBUTES, returnClusterObject)
+ dataVersionFilterLength = 0
readargs = []
for attr in attributes:
path = chip.interaction_model.AttributePathIBstruct.parse(
@@ -822,6 +864,30 @@
path = chip.interaction_model.AttributePathIBstruct.build(path)
readargs.append(ctypes.c_char_p(path))
+ if dataVersionFilters is not None:
+ dataVersionFilterLength = len(dataVersionFilters)
+ for f in dataVersionFilters:
+ filter = chip.interaction_model.DataVersionFilterIBstruct.parse(
+ b'\xff' * chip.interaction_model.DataVersionFilterIBstruct.sizeof())
+ if f.EndpointId is not None:
+ filter.EndpointId = f.EndpointId
+ else:
+ raise ValueError(
+ f"DataVersionFilter must provide EndpointId.")
+ if f.ClusterId is not None:
+ filter.ClusterId = f.ClusterId
+ else:
+ raise ValueError(
+ f"DataVersionFilter must provide ClusterId.")
+ if f.DataVersion is not None:
+ filter.DataVersion = f.DataVersion
+ else:
+ raise ValueError(
+ f"DataVersionFilter must provide DataVersion.")
+ filter = chip.interaction_model.DataVersionFilterIBstruct.build(
+ filter)
+ readargs.append(ctypes.c_char_p(filter))
+
ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction))
minInterval = 0
maxInterval = 0
@@ -844,7 +910,9 @@
ctypes.byref(readCallbackObj),
device,
ctypes.c_char_p(params),
- ctypes.c_size_t(len(attributes)), *readargs)
+ ctypes.c_size_t(len(attributes)),
+ ctypes.c_size_t(len(attributes) + dataVersionFilterLength),
+ *readargs)
transaction.SetClientObjPointers(readClientObj, readCallbackObj)
diff --git a/src/controller/python/chip/clusters/attribute.cpp b/src/controller/python/chip/clusters/attribute.cpp
index 2fd4195..02bdba6 100644
--- a/src/controller/python/chip/clusters/attribute.cpp
+++ b/src/controller/python/chip/clusters/attribute.cpp
@@ -42,6 +42,7 @@
chip::EndpointId endpointId;
chip::ClusterId clusterId;
chip::AttributeId attributeId;
+ chip::DataVersion dataVersion;
};
struct __attribute__((packed)) EventPath
@@ -51,6 +52,13 @@
chip::EventId eventId;
};
+struct __attribute__((packed)) DataVersionFilter
+{
+ chip::EndpointId endpointId;
+ chip::ClusterId clusterId;
+ chip::DataVersion dataVersion;
+};
+
using OnReadAttributeDataCallback = void (*)(PyObject * appContext, chip::DataVersion version, chip::EndpointId endpointId,
chip::ClusterId clusterId, chip::AttributeId attributeId,
std::underlying_type_t<Protocols::InteractionModel::Status> imstatus, uint8_t * data,
@@ -79,8 +87,7 @@
app::BufferedReadCallback * GetBufferedReadCallback() { return &mBufferedReadCallback; }
- void OnAttributeData(const ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
- const StatusIB & aStatus) override
+ void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) override
{
//
// We shouldn't be getting list item operations in the provided path since that should be handled by the buffered read
@@ -108,7 +115,7 @@
size = writer.GetLengthWritten();
}
- gOnReadAttributeDataCallback(mAppContext, aVersion, aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId,
+ gOnReadAttributeDataCallback(mAppContext, aPath.mDataVersion, aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId,
to_underlying(aStatus.mStatus), buffer.get(), size);
}
@@ -164,6 +171,11 @@
{
delete[] aReadPrepareParams.mpEventPathParamsList;
}
+
+ if (aReadPrepareParams.mpDataVersionFilterList != nullptr)
+ {
+ delete[] aReadPrepareParams.mpDataVersionFilterList;
+ }
}
void OnReportEnd() override { gOnReportEndCallback(mAppContext); }
@@ -200,7 +212,7 @@
uint16_t timedWriteTimeoutMs, size_t n, ...);
chip::ChipError::StorageType pychip_ReadClient_ReadAttributes(void * appContext, ReadClient ** pReadClient,
ReadClientCallback ** pCallback, DeviceProxy * device,
- uint8_t * readParamsBuf, size_t n, ...);
+ uint8_t * readParamsBuf, size_t n, size_t total, ...);
}
using OnWriteResponseCallback = void (*)(PyObject * appContext, chip::EndpointId endpointId, chip::ClusterId clusterId,
@@ -328,7 +340,7 @@
chip::ChipError::StorageType pychip_ReadClient_ReadAttributes(void * appContext, ReadClient ** pReadClient,
ReadClientCallback ** pCallback, DeviceProxy * device,
- uint8_t * readParamsBuf, size_t n, ...)
+ uint8_t * readParamsBuf, size_t n, size_t total, ...)
{
CHIP_ERROR err = CHIP_NO_ERROR;
PyReadAttributeParams pyParams = {};
@@ -337,10 +349,12 @@
std::unique_ptr<ReadClientCallback> callback = std::make_unique<ReadClientCallback>(appContext);
+ size_t m = total - n;
va_list args;
- va_start(args, n);
+ va_start(args, total);
std::unique_ptr<AttributePathParams[]> readPaths(new AttributePathParams[n]);
+ std::unique_ptr<chip::app::DataVersionFilter[]> dataVersionFilters(new chip::app::DataVersionFilter[m]);
std::unique_ptr<ReadClient> readClient;
{
@@ -355,6 +369,16 @@
}
}
+ for (size_t j = 0; j < m; j++)
+ {
+ void * filter = va_arg(args, void *);
+
+ python::DataVersionFilter filterObj;
+ memcpy(&filterObj, filter, sizeof(python::DataVersionFilter));
+
+ dataVersionFilters[j] = chip::app::DataVersionFilter(filterObj.endpointId, filterObj.clusterId, filterObj.dataVersion);
+ }
+
Optional<SessionHandle> session = device->GetSecureSession();
VerifyOrExit(session.HasValue(), err = CHIP_ERROR_NOT_CONNECTED);
@@ -366,7 +390,14 @@
ReadPrepareParams params(session.Value());
params.mpAttributePathParamsList = readPaths.get();
params.mAttributePathParamsListSize = n;
- params.mIsFabricFiltered = pyParams.isFabricFiltered;
+ if (m != 0)
+ {
+ params.mpDataVersionFilterList = dataVersionFilters.get();
+ params.mDataVersionFilterListSize = m;
+ }
+
+ params.mIsFabricFiltered = pyParams.isFabricFiltered;
+
if (pyParams.isSubscription)
{
params.mMinIntervalFloorSeconds = pyParams.minInterval;
diff --git a/src/controller/python/chip/interaction_model/__init__.py b/src/controller/python/chip/interaction_model/__init__.py
index 2bd9042..79e9464 100644
--- a/src/controller/python/chip/interaction_model/__init__.py
+++ b/src/controller/python/chip/interaction_model/__init__.py
@@ -22,7 +22,7 @@
"""Provides Python APIs for CHIP."""
import enum
-from .delegate import AttributePath, AttributePathIBstruct, EventPath, EventPathIBstruct
+from .delegate import AttributePath, AttributePathIBstruct, EventPath, EventPathIBstruct, DataVersionFilterIBstruct
from chip.exceptions import ChipStackException
@@ -53,7 +53,7 @@
UnsupportedRead = 0x8f
Deprecated90 = 0x90
Deprecated91 = 0x91
- Reserved92 = 0x92
+ DataVersionMismatch = 0x92
Deprecated93 = 0x93
Timeout = 0x94
Reserved95 = 0x95
diff --git a/src/controller/python/chip/interaction_model/delegate.py b/src/controller/python/chip/interaction_model/delegate.py
index 6ad6bd5..dbbd818 100644
--- a/src/controller/python/chip/interaction_model/delegate.py
+++ b/src/controller/python/chip/interaction_model/delegate.py
@@ -51,6 +51,7 @@
"EndpointId" / Int16ul,
"ClusterId" / Int32ul,
"AttributeId" / Int32ul,
+ "DataVersion" / Int32ul,
)
# EventPath should not contain padding
@@ -60,6 +61,12 @@
"EventId" / Int32ul,
)
+DataVersionFilterIBstruct = Struct(
+ "EndpointId" / Int16ul,
+ "ClusterId" / Int32ul,
+ "DataVersion" / Int32ul,
+)
+
@dataclass
class AttributePath:
diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py
index 4114faf..a33fe9b 100644
--- a/src/controller/python/test/test_scripts/base.py
+++ b/src/controller/python/test/test_scripts/base.py
@@ -190,8 +190,8 @@
data2 = await devCtrl2.ReadAttribute(nodeid, [(Clusters.OperationalCredentials.Attributes.NOCs)], fabricFiltered=False)
# Read out noclist from each fabric, and each should contain two NOCs.
- nocList1 = data1[0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.NOCs]
- nocList2 = data2[0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.NOCs]
+ nocList1 = data1[0][0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.NOCs]
+ nocList2 = data2[0][0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.NOCs]
if (len(nocList1) != 2 or len(nocList2) != 2):
self.logger.error("Got back invalid nocList")
@@ -201,8 +201,8 @@
data2 = await devCtrl2.ReadAttribute(nodeid, [(Clusters.OperationalCredentials.Attributes.CurrentFabricIndex)], fabricFiltered=False)
# Read out current fabric from each fabric, and both should be different.
- currentFabric1 = data1[0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.CurrentFabricIndex]
- currentFabric2 = data2[0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.CurrentFabricIndex]
+ currentFabric1 = data1[0][0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.CurrentFabricIndex]
+ currentFabric2 = data2[0][0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.CurrentFabricIndex]
if (currentFabric1 == currentFabric2):
self.logger.error(
"Got back fabric indices that match for two different fabrics!")
diff --git a/src/controller/python/test/test_scripts/cluster_objects.py b/src/controller/python/test/test_scripts/cluster_objects.py
index 02c1761..32ee18c 100644
--- a/src/controller/python/test/test_scripts/cluster_objects.py
+++ b/src/controller/python/test/test_scripts/cluster_objects.py
@@ -39,10 +39,11 @@
def VerifyDecodeSuccess(values):
- for endpoint in values:
- for cluster in values[endpoint]:
- for attribute in values[endpoint][cluster]:
- v = values[endpoint][cluster][attribute]
+ print(f"{values}")
+ for endpoint in values[0]:
+ for cluster in values[0][endpoint]:
+ for attribute in values[0][endpoint][cluster]:
+ v = values[0][endpoint][cluster][attribute]
print(f"EP{endpoint}/{attribute} = {v}")
if (isinstance(v, ValueDecodeFailure)):
if _IgnoreAttributeDecodeFailure((endpoint, cluster, attribute)):
@@ -50,7 +51,20 @@
f"Ignoring attribute decode failure for path {endpoint}/{attribute}")
else:
raise AssertionError(
- f"Cannot decode value for path {k}, got error: '{str(v.Data.Reason)}', raw TLV data: '{v.Data.TLVValue}'")
+ f"Cannot decode value for path {endpoint}/{attribute}, got error: '{str(v.Reason)}', raw TLV data: '{v.TLVValue}'")
+
+ for endpoint in values[1]:
+ for cluster in values[1][endpoint]:
+ for attribute in values[1][endpoint][cluster]:
+ v = values[1][endpoint][cluster][attribute]
+ print(f"EP{endpoint}/{attribute} version = {v}")
+ if (isinstance(v, ValueDecodeFailure)):
+ if _IgnoreAttributeDecodeFailure((endpoint, cluster, attribute)):
+ print(
+ f"Ignoring attribute version decode failure for path {endpoint}/{attribute}")
+ else:
+ raise AssertionError(
+ f"Cannot decode value for path {endpoint}/{attribute}, got error: '{str(v.Reason)}', raw TLV data: '{v.TLVValue}'")
def _AssumeEventsDecodeSuccess(values):
@@ -107,9 +121,9 @@
res = await devCtrl.WriteAttribute(nodeid=NODE_ID,
attributes=[
(0, Clusters.Basic.Attributes.NodeLabel(
- "Test")),
+ "Test"), 0),
(0, Clusters.Basic.Attributes.Location(
- "A loooong string"))
+ "A loooong string"), 0)
])
expectedRes = [
AttributeStatus(Path=AttributePath(EndpointId=0, ClusterId=40,
@@ -181,9 +195,9 @@
(0, Clusters.Basic.Attributes.HardwareVersion),
]
res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=req)
- if ((0 not in res) or (Clusters.Basic not in res[0]) or (len(res[0][Clusters.Basic]) != 3)):
+ if ((0 not in res[0]) or (Clusters.Basic not in res[0][0]) or (len(res[0][0][Clusters.Basic]) != 3)):
raise AssertionError(
- f"Got back {len(res)} data items instead of 3")
+ f"Got back {len(res[0])} data items instead of 3")
VerifyDecodeSuccess(res)
logger.info("2: Reading Ex Cx A*")
@@ -226,23 +240,23 @@
logger.info("7: Reading Chunked List")
res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=[(1, Clusters.TestCluster.Attributes.ListLongOctetString)])
- if res[1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListLongOctetString] != [b'0123456789abcdef' * 32] * 4:
+ if res[0][1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListLongOctetString] != [b'0123456789abcdef' * 32] * 4:
raise AssertionError("Unexpected read result")
logger.info("*: Getting current fabric index")
res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=[(0, Clusters.OperationalCredentials.Attributes.CurrentFabricIndex)])
- fabricIndex = res[0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.CurrentFabricIndex]
+ fabricIndex = res[0][0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.CurrentFabricIndex]
logger.info("8: Read without fabric filter")
res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=[(1, Clusters.TestCluster.Attributes.ListFabricScoped)], fabricFiltered=False)
- if len(res[1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListFabricScoped]) <= 1:
+ if len(res[0][1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListFabricScoped]) <= 1:
raise AssertionError("Expect more elements in the response")
logger.info("9: Read with fabric filter")
res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=[(1, Clusters.TestCluster.Attributes.ListFabricScoped)], fabricFiltered=True)
- if len(res[1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListFabricScoped]) != 1:
+ if len(res[0][1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListFabricScoped]) != 1:
raise AssertionError("Expect exact one element in the response")
- if res[1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListFabricScoped][0].fabricIndex != fabricIndex:
+ if res[0][1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListFabricScoped][0].fabricIndex != fabricIndex:
raise AssertionError(
"Expect the fabric index matches the one current reading")
@@ -307,7 +321,7 @@
await devCtrl.WriteAttribute(nodeid=NODE_ID,
attributes=[
(1, Clusters.TestCluster.Attributes.TimedWriteBoolean(
- True)),
+ True), 0),
],
timedRequestTimeoutMs=1000)
@@ -325,7 +339,7 @@
await devCtrl.WriteAttribute(nodeid=NODE_ID,
attributes=[
(1, Clusters.TestCluster.Attributes.TimedWriteBoolean(
- True)),
+ True), 0),
],
timedRequestTimeoutMs=10)
raise AssertionError("Timeout expected!")
@@ -347,13 +361,52 @@
await devCtrl.WriteAttribute(nodeid=NODE_ID,
attributes=[
(1, Clusters.TestCluster.Attributes.TimedWriteBoolean(
- True)),
+ True), 0),
])
raise AssertionError("The write request should be rejected.")
except ValueError:
pass
@classmethod
+ async def TestReadWriteAttributeRequestsWithVersion(cls, devCtrl):
+ logger.info("TestReadWriteAttributeRequestsWithVersion")
+ req = [
+ (0, Clusters.Basic.Attributes.VendorName)
+ ]
+ res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=req)
+ VerifyDecodeSuccess(res)
+ data_version = res[1][0][40][1]
+
+ res = await devCtrl.WriteAttribute(nodeid=NODE_ID,
+ attributes=[
+ (0, Clusters.Basic.Attributes.NodeLabel(
+ "Test"), 0)
+ ])
+ expectedRes = [
+ AttributeStatus(Path=AttributePath(EndpointId=0, ClusterId=40,
+ AttributeId=5), Status=chip.interaction_model.Status.Success),
+ ]
+
+ if res != expectedRes:
+ for i in range(len(res)):
+ if res[i] != expectedRes[i]:
+ logger.error(
+ f"Item {i} is not expected, expect {expectedRes[i]} got {res[i]}")
+ raise AssertionError("Write returned unexpected result.")
+
+ req = [
+ (0, Clusters.Basic.Attributes.VendorName),
+ ]
+ res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=req, dataVersionFilters=[(0, Clusters.Basic, data_version)])
+ VerifyDecodeSuccess(res)
+ new_data_version = res[1][0][40][1]
+ if (data_version + 1) != new_data_version:
+ raise AssertionError("Version mistmatch happens.")
+
+ res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=req, dataVersionFilters=[(0, Clusters.Basic, new_data_version)])
+ VerifyDecodeSuccess(res)
+
+ @classmethod
async def RunTest(cls, devCtrl):
try:
cls.TestAPI()
@@ -361,6 +414,7 @@
await cls.RoundTripTestWithBadEndpoint(devCtrl)
await cls.SendCommandWithResponse(devCtrl)
await cls.TestReadEventRequests(devCtrl, 1)
+ await cls.TestReadWriteAttributeRequestsWithVersion(devCtrl)
await cls.TestReadAttributeRequests(devCtrl)
await cls.TestSubscribeAttribute(devCtrl)
# Note: Write will change some attribute values, always put it after read tests
diff --git a/src/controller/python/test/test_scripts/network_commissioning.py b/src/controller/python/test/test_scripts/network_commissioning.py
index a76236a..523fe58 100644
--- a/src/controller/python/test/test_scripts/network_commissioning.py
+++ b/src/controller/python/test/test_scripts/network_commissioning.py
@@ -72,7 +72,7 @@
(endpointId, Clusters.NetworkCommissioning.Attributes.FeatureMap)],
returnClusterObject=True)
self.log_interface_basic_info(
- res[endpointId][Clusters.NetworkCommissioning])
+ res[0][endpointId][Clusters.NetworkCommissioning])
logger.info(f"Finished getting basic information of the endpoint")
# Scan networks
@@ -87,7 +87,7 @@
# Remove existing network
logger.info(f"Check network list")
res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True)
- networkList = res[endpointId][Clusters.NetworkCommissioning].networks
+ networkList = res[0][endpointId][Clusters.NetworkCommissioning].networks
logger.info(f"Got network list: {networkList}")
if len(networkList) != 0:
logger.info(f"Removing existing network")
@@ -110,7 +110,7 @@
logger.info(f"Check network list")
res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True)
- networkList = res[endpointId][Clusters.NetworkCommissioning].networks
+ networkList = res[0][endpointId][Clusters.NetworkCommissioning].networks
logger.info(f"Got network list: {networkList}")
if len(networkList) != 1:
raise AssertionError(
@@ -133,7 +133,7 @@
logger.info(f"Check network is connected")
res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True)
- networkList = res[endpointId][Clusters.NetworkCommissioning].networks
+ networkList = res[0][endpointId][Clusters.NetworkCommissioning].networks
logger.info(f"Got network list: {networkList}")
if len(networkList) != 1:
raise AssertionError(
@@ -156,7 +156,7 @@
(endpointId, Clusters.NetworkCommissioning.Attributes.FeatureMap)],
returnClusterObject=True)
self.log_interface_basic_info(
- res[endpointId][Clusters.NetworkCommissioning])
+ res[0][endpointId][Clusters.NetworkCommissioning])
logger.info(f"Finished getting basic information of the endpoint")
# Scan networks
@@ -171,7 +171,7 @@
# Remove existing network
logger.info(f"Check network list")
res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True)
- networkList = res[endpointId][Clusters.NetworkCommissioning].networks
+ networkList = res[0][endpointId][Clusters.NetworkCommissioning].networks
logger.info(f"Got network list: {networkList}")
if len(networkList) != 0:
logger.info(f"Removing existing network")
@@ -194,7 +194,7 @@
logger.info(f"Check network list")
res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True)
- networkList = res[endpointId][Clusters.NetworkCommissioning].networks
+ networkList = res[0][endpointId][Clusters.NetworkCommissioning].networks
logger.info(f"Got network list: {networkList}")
if len(networkList) != 1:
raise AssertionError(
@@ -233,6 +233,7 @@
try:
endpoints = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(Clusters.NetworkCommissioning.Attributes.FeatureMap)], returnClusterObject=True)
logger.info(endpoints)
+ endpoints = endpoints[0]
for endpoint, obj in endpoints.items():
clus = obj[Clusters.NetworkCommissioning]
if clus.featureMap == WIFI_NETWORK_FEATURE_MAP:
diff --git a/src/controller/tests/TestReadChunking.cpp b/src/controller/tests/TestReadChunking.cpp
index d704e16..9baab88 100644
--- a/src/controller/tests/TestReadChunking.cpp
+++ b/src/controller/tests/TestReadChunking.cpp
@@ -96,7 +96,7 @@
{
public:
TestReadCallback() : mBufferedCallback(*this) {}
- void OnAttributeData(const app::ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
+ void OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
const app::StatusIB & aStatus) override;
void OnDone() override;
@@ -108,7 +108,7 @@
app::BufferedReadCallback mBufferedCallback;
};
-void TestReadCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData,
+void TestReadCallback::OnAttributeData(const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
const app::StatusIB & aStatus)
{
if (aPath.mAttributeId != kTestListAttribute)
diff --git a/src/controller/tests/data_model/TestCommands.cpp b/src/controller/tests/data_model/TestCommands.cpp
index aedc62f..393c191 100644
--- a/src/controller/tests/data_model/TestCommands.cpp
+++ b/src/controller/tests/data_model/TestCommands.cpp
@@ -148,20 +148,6 @@
return Status::Success;
}
-
-CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered,
- const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports,
- AttributeValueEncoder::AttributeEncodeState * apEncoderState)
-{
- return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
-}
-
-CHIP_ERROR WriteSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteDataAttributePath & aPath,
- TLV::TLVReader & aReader, WriteHandler * aWriteHandler)
-{
- return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
-}
-
} // namespace app
} // namespace chip
diff --git a/src/controller/tests/data_model/TestRead.cpp b/src/controller/tests/data_model/TestRead.cpp
index dcf98a4..2451eda 100644
--- a/src/controller/tests/data_model/TestRead.cpp
+++ b/src/controller/tests/data_model/TestRead.cpp
@@ -36,6 +36,7 @@
namespace {
constexpr EndpointId kTestEndpointId = 1;
+constexpr DataVersion kDataVersion = 5;
enum ResponseDirective
{
@@ -49,34 +50,10 @@
namespace chip {
namespace app {
-
-void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, chip::TLV::TLVReader & aReader,
- CommandHandler * apCommandObj)
-{}
-
-InteractionModel::Status ServerClusterCommandExists(const ConcreteCommandPath & aCommandPath)
-{
- // Mock cluster catalog, only support commands on one cluster on one endpoint.
- using InteractionModel::Status;
-
- if (aCommandPath.mEndpointId != kTestEndpointId)
- {
- return Status::UnsupportedEndpoint;
- }
-
- if (aCommandPath.mClusterId != TestCluster::Id)
- {
- return Status::UnsupportedCluster;
- }
-
- return Status::Success;
-}
-
CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered,
const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports,
AttributeValueEncoder::AttributeEncodeState * apEncoderState)
{
-
if (responseDirective == kSendDataResponse)
{
if (aPath.mClusterId == app::Clusters::TestCluster::Id &&
@@ -88,7 +65,7 @@
aIsFabricFiltered, state);
return valueEncoder.EncodeList([aSubjectDescriptor](const auto & encoder) -> CHIP_ERROR {
- chip::app::Clusters::TestCluster::Structs::TestFabricScoped::Type val;
+ app::Clusters::TestCluster::Structs::TestFabricScoped::Type val;
val.fabricIndex = aSubjectDescriptor.fabricIndex;
ReturnErrorOnFailure(encoder.Encode(val));
val.fabricIndex = (val.fabricIndex == 1) ? 2 : 1;
@@ -114,7 +91,8 @@
i++;
}
- attributeData.DataVersion(0);
+ attributeData.DataVersion(kDataVersion);
+ ReturnErrorOnFailure(attributeData.GetError());
AttributePathIB::Builder & attributePath = attributeData.CreatePath();
attributePath.Endpoint(aPath.mEndpointId)
.Cluster(aPath.mClusterId)
@@ -123,7 +101,7 @@
ReturnErrorOnFailure(attributePath.GetError());
ReturnErrorOnFailure(DataModel::Encode(*(attributeData.GetWriter()),
- chip::TLV::ContextTag(chip::to_underlying(AttributeDataIB::Tag::kData)), value));
+ TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)), value));
ReturnErrorOnFailure(attributeData.EndOfAttributeDataIB().GetError());
return attributeReport.EndOfAttributeReportIB().GetError();
}
@@ -148,12 +126,17 @@
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
}
-CHIP_ERROR WriteSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteDataAttributePath & aPath,
- TLV::TLVReader & aReader, WriteHandler * aWriteHandler)
+bool IsClusterDataVersionEqual(EndpointId aEndpointId, ClusterId aClusterId, DataVersion aRequiredDataVersion)
{
- return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
+ if (aRequiredDataVersion == kDataVersion)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
-
} // namespace app
} // namespace chip
@@ -165,12 +148,14 @@
TestReadInteraction() {}
static void TestReadAttributeResponse(nlTestSuite * apSuite, void * apContext);
+ static void TestReadDataVersionFilter(nlTestSuite * apSuite, void * apContext);
static void TestReadAttributeError(nlTestSuite * apSuite, void * apContext);
static void TestReadAttributeTimeout(nlTestSuite * apSuite, void * apContext);
static void TestReadEventResponse(nlTestSuite * apSuite, void * apContext);
static void TestReadFabricScopedWithoutFabricFilter(nlTestSuite * apSuite, void * apContext);
static void TestReadFabricScopedWithFabricFilter(nlTestSuite * apSuite, void * apContext);
static void TestReadHandler_MultipleSubscriptions(nlTestSuite * apSuite, void * apContext);
+ static void TestReadHandler_MultipleSubscriptionsWithDataVersionFilter(nlTestSuite * apSuite, void * apContext);
static void TestReadHandlerResourceExhaustion_MultipleSubscriptions(nlTestSuite * apSuite, void * apContext);
static void TestReadHandlerResourceExhaustion_MultipleReads(nlTestSuite * apSuite, void * apContext);
@@ -187,9 +172,10 @@
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onSuccessCb = [apSuite, &onSuccessCbInvoked](const app::ConcreteAttributePath & attributePath, const auto & dataResponse) {
+ auto onSuccessCb = [apSuite, &onSuccessCbInvoked](const app::ConcreteDataAttributePath & attributePath,
+ const auto & dataResponse) {
uint8_t i = 0;
-
+ NL_TEST_ASSERT(apSuite, attributePath.mDataVersion == kDataVersion);
auto iter = dataResponse.begin();
while (iter.Next())
{
@@ -197,29 +183,72 @@
NL_TEST_ASSERT(apSuite, item.fabricIndex == i);
i++;
}
-
NL_TEST_ASSERT(apSuite, i == 4);
NL_TEST_ASSERT(apSuite, iter.GetStatus() == CHIP_NO_ERROR);
-
onSuccessCbInvoked = true;
};
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onFailureCb = [&onFailureCbInvoked](const app::ConcreteAttributePath * attributePath, CHIP_ERROR aError) {
+ auto onFailureCb = [&onFailureCbInvoked](const app::ConcreteDataAttributePath * attributePath, CHIP_ERROR aError) {
onFailureCbInvoked = true;
};
- chip::Controller::ReadAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
- &ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb);
+ Controller::ReadAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(&ctx.GetExchangeManager(), sessionHandle,
+ kTestEndpointId, onSuccessCb, onFailureCb);
ctx.DrainAndServiceIO();
- chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
ctx.DrainAndServiceIO();
NL_TEST_ASSERT(apSuite, onSuccessCbInvoked && !onFailureCbInvoked);
- NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0);
- NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
+ NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
+}
+
+void TestReadInteraction::TestReadDataVersionFilter(nlTestSuite * apSuite, void * apContext)
+{
+ TestContext & ctx = *static_cast<TestContext *>(apContext);
+ auto sessionHandle = ctx.GetSessionBobToAlice();
+ bool onSuccessCbInvoked = false, onFailureCbInvoked = false;
+
+ responseDirective = kSendDataResponse;
+
+ // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
+ // not safe to do so.
+ auto onSuccessCb = [apSuite, &onSuccessCbInvoked](const app::ConcreteDataAttributePath & attributePath,
+ const auto & dataResponse) {
+ uint8_t i = 0;
+ auto iter = dataResponse.begin();
+ while (iter.Next())
+ {
+ auto & item = iter.GetValue();
+ NL_TEST_ASSERT(apSuite, item.fabricIndex == i);
+ i++;
+ }
+ NL_TEST_ASSERT(apSuite, i == 4);
+ NL_TEST_ASSERT(apSuite, iter.GetStatus() == CHIP_NO_ERROR);
+ onSuccessCbInvoked = true;
+ };
+
+ // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
+ // not safe to do so.
+ auto onFailureCb = [&onFailureCbInvoked](const app::ConcreteDataAttributePath * attributePath, CHIP_ERROR aError) {
+ onFailureCbInvoked = true;
+ };
+
+ Optional<DataVersion> dataVersion(kDataVersion);
+ Controller::ReadAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
+ &ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb, true, dataVersion);
+
+ ctx.DrainAndServiceIO();
+ app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ ctx.DrainAndServiceIO();
+
+ NL_TEST_ASSERT(apSuite, !onSuccessCbInvoked && !onFailureCbInvoked);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
}
@@ -243,16 +272,16 @@
onFailureCbInvoked = true;
};
- chip::Controller::ReadEvent<TestCluster::Events::TestEvent::DecodableType>(&ctx.GetExchangeManager(), sessionHandle,
- kTestEndpointId, onSuccessCb, onFailureCb);
+ Controller::ReadEvent<TestCluster::Events::TestEvent::DecodableType>(&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId,
+ onSuccessCb, onFailureCb);
ctx.DrainAndServiceIO();
- chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
ctx.DrainAndServiceIO();
NL_TEST_ASSERT(apSuite, !onFailureCbInvoked);
- NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0);
- NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
}
@@ -266,27 +295,27 @@
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onSuccessCb = [&onSuccessCbInvoked](const app::ConcreteAttributePath & attributePath, const auto & dataResponse) {
+ auto onSuccessCb = [&onSuccessCbInvoked](const app::ConcreteDataAttributePath & attributePath, const auto & dataResponse) {
onSuccessCbInvoked = true;
};
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onFailureCb = [&onFailureCbInvoked, apSuite](const app::ConcreteAttributePath * attributePath, CHIP_ERROR aError) {
+ auto onFailureCb = [&onFailureCbInvoked, apSuite](const app::ConcreteDataAttributePath * attributePath, CHIP_ERROR aError) {
NL_TEST_ASSERT(apSuite, aError.IsIMStatus() && app::StatusIB(aError).mStatus == Protocols::InteractionModel::Status::Busy);
onFailureCbInvoked = true;
};
- chip::Controller::ReadAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
- &ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb);
+ Controller::ReadAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(&ctx.GetExchangeManager(), sessionHandle,
+ kTestEndpointId, onSuccessCb, onFailureCb);
ctx.DrainAndServiceIO();
- chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
ctx.DrainAndServiceIO();
NL_TEST_ASSERT(apSuite, !onSuccessCbInvoked && onFailureCbInvoked);
- NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0);
- NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
}
@@ -300,19 +329,19 @@
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onSuccessCb = [&onSuccessCbInvoked](const app::ConcreteAttributePath & attributePath, const auto & dataResponse) {
+ auto onSuccessCb = [&onSuccessCbInvoked](const app::ConcreteDataAttributePath & attributePath, const auto & dataResponse) {
onSuccessCbInvoked = true;
};
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onFailureCb = [&onFailureCbInvoked, apSuite](const app::ConcreteAttributePath * attributePath, CHIP_ERROR aError) {
+ auto onFailureCb = [&onFailureCbInvoked, apSuite](const app::ConcreteDataAttributePath * attributePath, CHIP_ERROR aError) {
NL_TEST_ASSERT(apSuite, aError == CHIP_ERROR_TIMEOUT);
onFailureCbInvoked = true;
};
- chip::Controller::ReadAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
- &ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb);
+ Controller::ReadAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(&ctx.GetExchangeManager(), sessionHandle,
+ kTestEndpointId, onSuccessCb, onFailureCb);
ctx.DrainAndServiceIO();
@@ -330,12 +359,12 @@
// NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 1);
ctx.DrainAndServiceIO();
- chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
ctx.DrainAndServiceIO();
ctx.ExpireSessionAliceToBob();
- NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
//
// Let's put back the sessions so that the next tests (which assume a valid initialized set of sessions)
@@ -361,13 +390,13 @@
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onSuccessCb = [&numSuccessCalls](const app::ConcreteAttributePath & attributePath, const auto & dataResponse) {
+ auto onSuccessCb = [&numSuccessCalls](const app::ConcreteDataAttributePath & attributePath, const auto & dataResponse) {
numSuccessCalls++;
};
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onFailureCb = [&apSuite](const app::ConcreteAttributePath * attributePath, CHIP_ERROR aError) {
+ auto onFailureCb = [&apSuite](const app::ConcreteDataAttributePath * attributePath, CHIP_ERROR aError) {
//
// We shouldn't be encountering any failures in this test.
//
@@ -384,7 +413,7 @@
for (int i = 0; i < (CHIP_IM_MAX_NUM_READ_HANDLER + 1); i++)
{
NL_TEST_ASSERT(apSuite,
- chip::Controller::SubscribeAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
+ Controller::SubscribeAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb, 0, 10,
onSubscriptionEstablishedCb, false, true) == CHIP_NO_ERROR);
}
@@ -396,7 +425,71 @@
for (int i = 0; i < 10 && (numSubscriptionEstablishedCalls != (CHIP_IM_MAX_NUM_READ_HANDLER + 1)); i++)
{
ctx.DrainAndServiceIO();
- chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ ctx.DrainAndServiceIO();
+ }
+
+ NL_TEST_ASSERT(apSuite, numSuccessCalls == (CHIP_IM_MAX_NUM_READ_HANDLER + 1));
+ NL_TEST_ASSERT(apSuite, numSubscriptionEstablishedCalls == (CHIP_IM_MAX_NUM_READ_HANDLER + 1));
+
+ app::InteractionModelEngine::GetInstance()->ShutdownActiveReads();
+
+ //
+ // TODO: Figure out why I cannot enable this line below.
+ //
+ // NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
+}
+
+void TestReadInteraction::TestReadHandler_MultipleSubscriptionsWithDataVersionFilter(nlTestSuite * apSuite, void * apContext)
+{
+ TestContext & ctx = *static_cast<TestContext *>(apContext);
+ auto sessionHandle = ctx.GetSessionBobToAlice();
+ uint32_t numSuccessCalls = 0;
+ uint32_t numSubscriptionEstablishedCalls = 0;
+
+ responseDirective = kSendDataResponse;
+
+ // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
+ // not safe to do so.
+ auto onSuccessCb = [apSuite, &numSuccessCalls](const app::ConcreteDataAttributePath & attributePath,
+ const auto & dataResponse) {
+ NL_TEST_ASSERT(apSuite, attributePath.mDataVersion == kDataVersion);
+ numSuccessCalls++;
+ };
+
+ // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
+ // not safe to do so.
+ auto onFailureCb = [&apSuite](const app::ConcreteDataAttributePath * attributePath, CHIP_ERROR aError) {
+ //
+ // We shouldn't be encountering any failures in this test.
+ //
+ NL_TEST_ASSERT(apSuite, false);
+ };
+
+ auto onSubscriptionEstablishedCb = [&numSubscriptionEstablishedCalls]() { numSubscriptionEstablishedCalls++; };
+
+ //
+ // Try to issue parallel subscriptions that will exceed the value for CHIP_IM_MAX_NUM_READ_HANDLER.
+ // If heap allocation is correctly setup, this should result in it successfully servicing more than the number
+ // present in that define.
+ //
+ chip::Optional<chip::DataVersion> dataVersion(1);
+ for (int i = 0; i < (CHIP_IM_MAX_NUM_READ_HANDLER + 1); i++)
+ {
+ NL_TEST_ASSERT(apSuite,
+ Controller::SubscribeAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
+ &ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb, 0, 10,
+ onSubscriptionEstablishedCb, false, true, dataVersion) == CHIP_NO_ERROR);
+ }
+
+ //
+ // It may take a couple of service calls since we may hit the limit of CHIP_IM_MAX_REPORTS_IN_FLIGHT
+ // reports.
+ //
+ for (int i = 0; i < 10 && (numSubscriptionEstablishedCalls != (CHIP_IM_MAX_NUM_READ_HANDLER + 1)); i++)
+ {
+ ctx.DrainAndServiceIO();
+ app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
ctx.DrainAndServiceIO();
}
@@ -423,13 +516,13 @@
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onSuccessCb = [&numSuccessCalls](const app::ConcreteAttributePath & attributePath, const auto & dataResponse) {
+ auto onSuccessCb = [&numSuccessCalls](const app::ConcreteDataAttributePath & attributePath, const auto & dataResponse) {
numSuccessCalls++;
};
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onFailureCb = [&apSuite, &numFailureCalls](const app::ConcreteAttributePath * attributePath, CHIP_ERROR aError) {
+ auto onFailureCb = [&apSuite, &numFailureCalls](const app::ConcreteDataAttributePath * attributePath, CHIP_ERROR aError) {
numFailureCalls++;
NL_TEST_ASSERT(apSuite, aError == CHIP_IM_GLOBAL_STATUS(ResourceExhausted));
@@ -443,14 +536,13 @@
// since the second subscription below should fail correctly.
//
app::InteractionModelEngine::GetInstance()->SetHandlerCapacity(2);
-
NL_TEST_ASSERT(apSuite,
- chip::Controller::SubscribeAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
+ Controller::SubscribeAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb, 0, 10,
onSubscriptionEstablishedCb, false, true) == CHIP_NO_ERROR);
NL_TEST_ASSERT(apSuite,
- chip::Controller::SubscribeAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
+ Controller::SubscribeAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb, 0, 10,
onSubscriptionEstablishedCb, false, true) == CHIP_NO_ERROR);
@@ -461,7 +553,7 @@
for (int i = 0; i < 10; i++)
{
ctx.DrainAndServiceIO();
- chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
ctx.DrainAndServiceIO();
}
@@ -490,13 +582,13 @@
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onSuccessCb = [&numSuccessCalls](const app::ConcreteAttributePath & attributePath, const auto & dataResponse) {
+ auto onSuccessCb = [&numSuccessCalls](const app::ConcreteDataAttributePath & attributePath, const auto & dataResponse) {
numSuccessCalls++;
};
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onFailureCb = [&apSuite, &numFailureCalls](const app::ConcreteAttributePath * attributePath, CHIP_ERROR aError) {
+ auto onFailureCb = [&apSuite, &numFailureCalls](const app::ConcreteDataAttributePath * attributePath, CHIP_ERROR aError) {
numFailureCalls++;
NL_TEST_ASSERT(apSuite, aError == CHIP_IM_GLOBAL_STATUS(ResourceExhausted));
@@ -506,7 +598,7 @@
app::InteractionModelEngine::GetInstance()->SetHandlerCapacity(0);
NL_TEST_ASSERT(apSuite,
- chip::Controller::ReadAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
+ Controller::ReadAttribute<TestCluster::Attributes::ListStructOctetString::TypeInfo>(
&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb) == CHIP_NO_ERROR);
//
@@ -516,7 +608,7 @@
for (int i = 0; i < 10; i++)
{
ctx.DrainAndServiceIO();
- chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
ctx.DrainAndServiceIO();
}
@@ -552,7 +644,8 @@
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onSuccessCb = [apSuite, &onSuccessCbInvoked](const app::ConcreteAttributePath & attributePath, const auto & dataResponse) {
+ auto onSuccessCb = [apSuite, &onSuccessCbInvoked](const app::ConcreteDataAttributePath & attributePath,
+ const auto & dataResponse) {
size_t len = 0;
NL_TEST_ASSERT(apSuite, dataResponse.ComputeSize(&len) == CHIP_NO_ERROR);
@@ -563,20 +656,20 @@
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onFailureCb = [&onFailureCbInvoked](const app::ConcreteAttributePath * attributePath, CHIP_ERROR aError) {
+ auto onFailureCb = [&onFailureCbInvoked](const app::ConcreteDataAttributePath * attributePath, CHIP_ERROR aError) {
onFailureCbInvoked = true;
};
- chip::Controller::ReadAttribute<TestCluster::Attributes::ListFabricScoped::TypeInfo>(
+ Controller::ReadAttribute<TestCluster::Attributes::ListFabricScoped::TypeInfo>(
&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb, false /* fabric filtered */);
ctx.DrainAndServiceIO();
- chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
ctx.DrainAndServiceIO();
NL_TEST_ASSERT(apSuite, onSuccessCbInvoked && !onFailureCbInvoked);
- NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0);
- NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
}
@@ -600,7 +693,8 @@
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onSuccessCb = [apSuite, &onSuccessCbInvoked](const app::ConcreteAttributePath & attributePath, const auto & dataResponse) {
+ auto onSuccessCb = [apSuite, &onSuccessCbInvoked](const app::ConcreteDataAttributePath & attributePath,
+ const auto & dataResponse) {
size_t len = 0;
NL_TEST_ASSERT(apSuite, dataResponse.ComputeSize(&len) == CHIP_NO_ERROR);
@@ -620,20 +714,20 @@
// Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's
// not safe to do so.
- auto onFailureCb = [&onFailureCbInvoked](const app::ConcreteAttributePath * attributePath, CHIP_ERROR aError) {
+ auto onFailureCb = [&onFailureCbInvoked](const app::ConcreteDataAttributePath * attributePath, CHIP_ERROR aError) {
onFailureCbInvoked = true;
};
- chip::Controller::ReadAttribute<TestCluster::Attributes::ListFabricScoped::TypeInfo>(
+ Controller::ReadAttribute<TestCluster::Attributes::ListFabricScoped::TypeInfo>(
&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb, true /* fabric filtered */);
ctx.DrainAndServiceIO();
- chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
+ app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run();
ctx.DrainAndServiceIO();
NL_TEST_ASSERT(apSuite, onSuccessCbInvoked && !onFailureCbInvoked);
- NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0);
- NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0);
+ NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0);
NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
}
@@ -641,12 +735,14 @@
const nlTest sTests[] =
{
NL_TEST_DEF("TestReadAttributeResponse", TestReadInteraction::TestReadAttributeResponse),
+ NL_TEST_DEF("TestReadDataVersionFilter", TestReadInteraction::TestReadDataVersionFilter),
NL_TEST_DEF("TestReadEventResponse", TestReadInteraction::TestReadEventResponse),
NL_TEST_DEF("TestReadAttributeError", TestReadInteraction::TestReadAttributeError),
NL_TEST_DEF("TestReadFabricScopedWithoutFabricFilter", TestReadInteraction::TestReadFabricScopedWithoutFabricFilter),
NL_TEST_DEF("TestReadFabricScopedWithFabricFilter", TestReadInteraction::TestReadFabricScopedWithFabricFilter),
NL_TEST_DEF("TestReadAttributeTimeout", TestReadInteraction::TestReadAttributeTimeout),
NL_TEST_DEF("TestReadHandler_MultipleSubscriptions", TestReadInteraction::TestReadHandler_MultipleSubscriptions),
+ NL_TEST_DEF("TestReadHandler_MultipleSubscriptionsWithDataVersionFilter", TestReadInteraction::TestReadHandler_MultipleSubscriptionsWithDataVersionFilter),
NL_TEST_DEF("TestReadHandlerResourceExhaustion_MultipleSubscriptions", TestReadInteraction::TestReadHandlerResourceExhaustion_MultipleSubscriptions),
NL_TEST_DEF("TestReadHandlerResourceExhaustion_MultipleReads", TestReadInteraction::TestReadHandlerResourceExhaustion_MultipleReads),
NL_TEST_SENTINEL()
diff --git a/src/controller/tests/data_model/TestWrite.cpp b/src/controller/tests/data_model/TestWrite.cpp
index e97feae..c13f547 100644
--- a/src/controller/tests/data_model/TestWrite.cpp
+++ b/src/controller/tests/data_model/TestWrite.cpp
@@ -53,35 +53,6 @@
namespace chip {
namespace app {
-void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, chip::TLV::TLVReader & aReader,
- CommandHandler * apCommandObj)
-{}
-
-InteractionModel::Status ServerClusterCommandExists(const ConcreteCommandPath & aCommandPath)
-{
- // Mock cluster catalog, only support commands on one cluster on one endpoint.
- using InteractionModel::Status;
-
- if (aCommandPath.mEndpointId != kTestEndpointId)
- {
- return Status::UnsupportedEndpoint;
- }
-
- if (aCommandPath.mClusterId != TestCluster::Id)
- {
- return Status::UnsupportedCluster;
- }
-
- return Status::Success;
-}
-
-CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered,
- const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports,
- AttributeValueEncoder::AttributeEncodeState * apEncoderState)
-{
- return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
-}
-
CHIP_ERROR WriteSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteDataAttributePath & aPath,
TLV::TLVReader & aReader, WriteHandler * aWriteHandler)
{
diff --git a/src/darwin/Framework/CHIP/CHIPDevice.mm b/src/darwin/Framework/CHIP/CHIPDevice.mm
index d75bd39..6dd9f6e 100644
--- a/src/darwin/Framework/CHIP/CHIPDevice.mm
+++ b/src/darwin/Framework/CHIP/CHIPDevice.mm
@@ -96,8 +96,7 @@
void OnReportEnd() override;
- void OnAttributeData(
- const ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData, const StatusIB & aStatus) override;
+ void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) override;
void OnError(CHIP_ERROR aError) override;
@@ -215,7 +214,7 @@
}
void SubscriptionCallback::OnAttributeData(
- const ConcreteDataAttributePath & aPath, DataVersion aVersion, TLV::TLVReader * apData, const StatusIB & aStatus)
+ const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus)
{
if (aPath.IsListItemOperation()) {
ReportError(CHIP_ERROR_INCORRECT_STATE);
diff --git a/src/lib/core/CHIPError.cpp b/src/lib/core/CHIPError.cpp
index ea22ad3..f3dd028 100644
--- a/src/lib/core/CHIPError.cpp
+++ b/src/lib/core/CHIPError.cpp
@@ -545,9 +545,6 @@
case CHIP_ERROR_INCOMPATIBLE_SCHEMA_VERSION.AsInteger():
desc = "Incompatible data schema version";
break;
- case CHIP_ERROR_MISMATCH_UPDATE_REQUIRED_VERSION.AsInteger():
- desc = "Update Required Version mismatch";
- break;
case CHIP_ERROR_ACCESS_DENIED.AsInteger():
desc = "The CHIP message is not granted access";
break;
diff --git a/src/lib/core/CHIPError.h b/src/lib/core/CHIPError.h
index c5eba86..7420d85 100644
--- a/src/lib/core/CHIPError.h
+++ b/src/lib/core/CHIPError.h
@@ -1895,14 +1895,6 @@
#define CHIP_ERROR_INCOMPATIBLE_SCHEMA_VERSION CHIP_CORE_ERROR(0xa3)
/**
- * @def CHIP_ERROR_MISMATCH_UPDATE_REQUIRED_VERSION
- *
- * @brief
- * Encountered a mismatch between update required version and current version
- */
-#define CHIP_ERROR_MISMATCH_UPDATE_REQUIRED_VERSION CHIP_CORE_ERROR(0xa4)
-
-/**
* @def CHIP_ERROR_ACCESS_DENIED
*
* @brief
diff --git a/src/lib/core/DataModelTypes.h b/src/lib/core/DataModelTypes.h
index d931944..3dccb05 100644
--- a/src/lib/core/DataModelTypes.h
+++ b/src/lib/core/DataModelTypes.h
@@ -48,7 +48,7 @@
constexpr EndpointId kInvalidEndpointId = 0xFFFF;
constexpr EndpointId kRootEndpointId = 0;
constexpr ListIndex kInvalidListIndex = 0xFFFF; // List index is a uint16 thus 0xFFFF is a invalid list index.
-constexpr DataVersion kUndefinedDataVersion = 0;
+
// These are MEIs, 0xFFFF is not a valid manufacturer code,
// thus 0xFFFF'FFFF is not a valid MEI.
static constexpr ClusterId kInvalidClusterId = 0xFFFF'FFFF;
diff --git a/src/lib/core/tests/TestCHIPErrorStr.cpp b/src/lib/core/tests/TestCHIPErrorStr.cpp
index 32a71ff..1f8e5e7 100644
--- a/src/lib/core/tests/TestCHIPErrorStr.cpp
+++ b/src/lib/core/tests/TestCHIPErrorStr.cpp
@@ -210,7 +210,6 @@
CHIP_ERROR_PROFILE_STRING_CONTEXT_ALREADY_REGISTERED,
CHIP_ERROR_PROFILE_STRING_CONTEXT_NOT_REGISTERED,
CHIP_ERROR_INCOMPATIBLE_SCHEMA_VERSION,
- CHIP_ERROR_MISMATCH_UPDATE_REQUIRED_VERSION,
CHIP_ERROR_ACCESS_DENIED,
CHIP_ERROR_UNKNOWN_RESOURCE_ID,
CHIP_ERROR_VERSION_MISMATCH,
diff --git a/src/platform/CYW30739/args.gni b/src/platform/CYW30739/args.gni
index a49440e..43f6ec4 100644
--- a/src/platform/CYW30739/args.gni
+++ b/src/platform/CYW30739/args.gni
@@ -29,6 +29,8 @@
chip_device_platform = "cyw30739"
chip_mdns = "platform"
+# Trying to fit into the available flash by disabling optional logging for now
+chip_detail_logging = false
chip_enable_openthread = true
lwip_platform = "cyw30739"