Skip to content

Commit

Permalink
Add a way to run custom cluster logic for attribute write
Browse files Browse the repository at this point in the history
  • Loading branch information
yufengwangca committed Nov 9, 2021
1 parent a04576d commit 8e62ee4
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 61 deletions.
21 changes: 21 additions & 0 deletions src/app/AttributeAccessInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,27 @@ class AttributeAccessInterface
*/
virtual CHIP_ERROR Read(const ConcreteAttributePath & aPath, AttributeValueEncoder & aEncoder) = 0;

/**
* Callback for writing attributes.
*
* @param [in] aPath indicates which exact data is being write.
* @param [in] aTLVReader A pointer to a TLVReader, which should point to the beginning
* of this AttributeDataElement to write.
* @param [out] aDataWrite whether we actually tried to write data. If
* this function returns success and aDataWrite is
* false, the AttributeAccessInterface did not try
* to write any data. In this case, normal attribute
* access will happen for the write. This may involve
* writing to the attribute store or external attribute
* callbacks.
*/
virtual CHIP_ERROR Write(const ConcreteAttributePath & aPath, TLV::TLVReader & aReader, bool * aDataWrite)
{
*aDataWrite = false;

return CHIP_NO_ERROR;
}

/**
* Mechanism for keeping track of a chain of AttributeAccessInterfaces.
*/
Expand Down
163 changes: 132 additions & 31 deletions src/app/clusters/test-cluster-server/test-cluster-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ using namespace chip::app::Clusters::TestCluster::Attributes;
constexpr const char * kErrorStr = "Test Cluster: List Octet cluster (0x%02x) Error setting '%s' attribute: 0x%02x";
#endif // CHIP_CLUSTER_CONFIG_ENABLE_COMPLEX_ATTRIBUTE_READ

constexpr uint8_t kAttributeListLength = 4;
constexpr uint8_t kAttributeEntryLength = 6;

namespace {

class TestAttrAccess : public AttributeAccessInterface
Expand All @@ -53,15 +56,24 @@ class TestAttrAccess : public AttributeAccessInterface
TestAttrAccess() : AttributeAccessInterface(Optional<EndpointId>::Missing(), TestCluster::Id) {}

CHIP_ERROR Read(const ConcreteAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
CHIP_ERROR Write(const ConcreteAttributePath & aPath, TLV::TLVReader & aReader, bool * aDataWrite) override;

private:
CHIP_ERROR ReadListInt8uAttribute(AttributeValueEncoder & aEncoder);
CHIP_ERROR WriteListInt8uAttribute(TLV::TLVReader & aReader);
CHIP_ERROR ReadListOctetStringAttribute(AttributeValueEncoder & aEncoder);
CHIP_ERROR WriteListOctetStringAttribute(TLV::TLVReader & aReader);
CHIP_ERROR ReadListStructOctetStringAttribute(AttributeValueEncoder & aEncoder);
CHIP_ERROR WriteListStructOctetStringAttribute(TLV::TLVReader & aReader);
CHIP_ERROR ReadListNullablesAndOptionalsStructAttribute(AttributeValueEncoder & aEncoder);
CHIP_ERROR WrtieListNullablesAndOptionalsStructAttribute(TLV::TLVReader & aReader);
};

TestAttrAccess gAttrAccess;
uint8_t gListUint8Data[kAttributeListLength];
char gListOctetStringData[kAttributeListLength][kAttributeEntryLength];
char gListOperationalCert[kAttributeListLength][kAttributeEntryLength];
Structs::TestListStructOctet::Type listStructOctetStringData[kAttributeListLength];

CHIP_ERROR TestAttrAccess::Read(const ConcreteAttributePath & aPath, AttributeValueEncoder & aEncoder)
{
Expand All @@ -87,6 +99,33 @@ CHIP_ERROR TestAttrAccess::Read(const ConcreteAttributePath & aPath, AttributeVa
return CHIP_NO_ERROR;
}

CHIP_ERROR TestAttrAccess::Write(const ConcreteAttributePath & aPath, TLV::TLVReader & aReader, bool * aDataWrite)
{
*aDataWrite = true;

switch (aPath.mAttributeId)
{
case ListInt8u::Id: {
return WriteListInt8uAttribute(aReader);
}
case ListOctetString::Id: {
return WriteListOctetStringAttribute(aReader);
}
case ListStructOctetString::Id: {
return WriteListStructOctetStringAttribute(aReader);
}
case ListNullablesAndOptionalsStruct::Id: {
return WrtieListNullablesAndOptionalsStructAttribute(aReader);
}
default: {
*aDataWrite = false;
break;
}
}

return CHIP_NO_ERROR;
}

#if !CHIP_CLUSTER_CONFIG_ENABLE_COMPLEX_ATTRIBUTE_READ
EmberAfStatus writeAttribute(EndpointId endpoint, AttributeId attributeId, uint8_t * buffer, int32_t index = -1)
{
Expand All @@ -113,14 +152,13 @@ EmberAfStatus writeTestListInt8uAttribute(EndpointId endpoint)
EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
AttributeId attributeId = ZCL_LIST_ATTRIBUTE_ID;

uint16_t attributeCount = 4;
for (uint8_t index = 0; index < attributeCount; index++)
for (uint8_t index = 0; index < kAttributeListLength; index++)
{
status = writeAttribute(endpoint, attributeId, (uint8_t *) &index, index);
VerifyOrReturnError(status == EMBER_ZCL_STATUS_SUCCESS, status);
}

status = writeAttribute(endpoint, attributeId, (uint8_t *) &attributeCount);
status = writeAttribute(endpoint, attributeId, (uint8_t *) &kAttributeListLength);
VerifyOrReturnError(status == EMBER_ZCL_STATUS_SUCCESS, status);
return status;
}
Expand All @@ -129,34 +167,54 @@ EmberAfStatus writeTestListInt8uAttribute(EndpointId endpoint)
CHIP_ERROR TestAttrAccess::ReadListInt8uAttribute(AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([](const TagBoundEncoder & encoder) -> CHIP_ERROR {
constexpr uint16_t attributeCount = 4;
for (uint8_t index = 0; index < attributeCount; index++)
for (uint8_t index = 0; index < kAttributeListLength; index++)
{
ReturnErrorOnFailure(encoder.Encode(index));
ReturnErrorOnFailure(encoder.Encode(gListUint8Data[index]));
}
return CHIP_NO_ERROR;
});
}

CHIP_ERROR TestAttrAccess::WriteListInt8uAttribute(TLV::TLVReader & aReader)
{
DataModel::DecodableList<uint8_t> list;

ReturnErrorOnFailure(DataModel::Decode(aReader, list));

uint8_t index = 0;
auto iter = list.begin();
while (iter.Next())
{
auto & entry = iter.GetValue();
gListUint8Data[index++] = entry;
}

if (iter.GetStatus() != CHIP_NO_ERROR)
{
return CHIP_ERROR_INVALID_DATA_LIST;
}

return CHIP_NO_ERROR;
}

#if !CHIP_CLUSTER_CONFIG_ENABLE_COMPLEX_ATTRIBUTE_READ
EmberAfStatus writeTestListOctetAttribute(EndpointId endpoint)
{
EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
AttributeId attributeId = ZCL_LIST_OCTET_STRING_ATTRIBUTE_ID;

uint16_t attributeCount = 4;
char data[6] = { 'T', 'e', 's', 't', 'N', '\0' };
ByteSpan span = ByteSpan(Uint8::from_char(data), strlen(data));
char data[6] = { 'T', 'e', 's', 't', 'N', '\0' };
ByteSpan span = ByteSpan(Uint8::from_char(data), strlen(data));

for (uint8_t index = 0; index < attributeCount; index++)
for (uint8_t index = 0; index < kAttributeListLength; index++)
{
sprintf(data + strlen(data) - 1, "%d", index);

status = writeAttribute(endpoint, attributeId, (uint8_t *) &span, index);
VerifyOrReturnError(status == EMBER_ZCL_STATUS_SUCCESS, status);
}

status = writeAttribute(endpoint, attributeId, (uint8_t *) &attributeCount);
status = writeAttribute(endpoint, attributeId, (uint8_t *) &kAttributeListLength);
VerifyOrReturnError(status == EMBER_ZCL_STATUS_SUCCESS, status);
return status;
}
Expand All @@ -165,30 +223,45 @@ EmberAfStatus writeTestListOctetAttribute(EndpointId endpoint)
CHIP_ERROR TestAttrAccess::ReadListOctetStringAttribute(AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([](const TagBoundEncoder & encoder) -> CHIP_ERROR {
constexpr uint16_t attributeCount = 4;
char data[6] = { 'T', 'e', 's', 't', 'N', '\0' };

for (uint8_t index = 0; index < attributeCount; index++)
for (uint8_t index = 0; index < kAttributeListLength; index++)
{
snprintf(data + strlen(data) - 1, 2, "%d", index);
ByteSpan span(Uint8::from_char(data), strlen(data));
ByteSpan span(Uint8::from_char(gListOctetStringData[index]), strlen(gListOctetStringData[index]));
ReturnErrorOnFailure(encoder.Encode(span));
}
return CHIP_NO_ERROR;
});
}

CHIP_ERROR TestAttrAccess::WriteListOctetStringAttribute(TLV::TLVReader & aReader)
{
DataModel::DecodableList<ByteSpan> list;

ReturnErrorOnFailure(DataModel::Decode(aReader, list));

uint8_t index = 0;
auto iter = list.begin();
while (iter.Next())
{
const auto & entry = iter.GetValue();

VerifyOrReturnError(index < kAttributeListLength, CHIP_ERROR_BUFFER_TOO_SMALL);
Platform::CopyString(gListOctetStringData[index], sizeof(gListOctetStringData[index]), entry);
index++;
}

return iter.GetStatus();
}

#if !CHIP_CLUSTER_CONFIG_ENABLE_COMPLEX_ATTRIBUTE_READ
EmberAfStatus writeTestListStructOctetAttribute(EndpointId endpoint)
{
EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
AttributeId attributeId = ZCL_LIST_STRUCT_OCTET_STRING_ATTRIBUTE_ID;

uint16_t attributeCount = 4;
char data[6] = { 'T', 'e', 's', 't', 'N', '\0' };
ByteSpan span = ByteSpan(Uint8::from_char(data), strlen(data));
char data[6] = { 'T', 'e', 's', 't', 'N', '\0' };
ByteSpan span = ByteSpan(Uint8::from_char(data), strlen(data));

for (uint8_t index = 0; index < attributeCount; index++)
for (uint8_t index = 0; index < kAttributeListLength; index++)
{
sprintf(data + strlen(data) - 1, "%d", index);

Expand All @@ -200,7 +273,7 @@ EmberAfStatus writeTestListStructOctetAttribute(EndpointId endpoint)
VerifyOrReturnError(status == EMBER_ZCL_STATUS_SUCCESS, status);
}

status = writeAttribute(endpoint, attributeId, (uint8_t *) &attributeCount);
status = writeAttribute(endpoint, attributeId, (uint8_t *) &kAttributeListLength);
VerifyOrReturnError(status == EMBER_ZCL_STATUS_SUCCESS, status);
return status;
}
Expand All @@ -209,23 +282,46 @@ EmberAfStatus writeTestListStructOctetAttribute(EndpointId endpoint)
CHIP_ERROR TestAttrAccess::ReadListStructOctetStringAttribute(AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([](const TagBoundEncoder & encoder) -> CHIP_ERROR {
constexpr uint16_t attributeCount = 4;
char data[6] = { 'T', 'e', 's', 't', 'N', '\0' };

for (uint8_t index = 0; index < attributeCount; index++)
for (uint8_t index = 0; index < kAttributeListLength; index++)
{
snprintf(data + strlen(data) - 1, 2, "%d", index);
ByteSpan span(Uint8::from_char(data), strlen(data));

Structs::TestListStructOctet::Type structOctet;
structOctet.fabricIndex = index;
structOctet.operationalCert = span;
structOctet.fabricIndex = listStructOctetStringData[index].fabricIndex;
structOctet.operationalCert = listStructOctetStringData[index].operationalCert;
ReturnErrorOnFailure(encoder.Encode(structOctet));
}

return CHIP_NO_ERROR;
});
}

CHIP_ERROR TestAttrAccess::WriteListStructOctetStringAttribute(TLV::TLVReader & aReader)
{
DataModel::DecodableList<Structs::TestListStructOctet::Type> list;

ReturnErrorOnFailure(DataModel::Decode(aReader, list));

uint8_t index = 0;
auto iter = list.begin();
while (iter.Next())
{
const auto & entry = iter.GetValue();

VerifyOrReturnError(index < kAttributeListLength, CHIP_ERROR_BUFFER_TOO_SMALL);
Platform::CopyString(gListOperationalCert[index], sizeof(gListOperationalCert[index]), entry.operationalCert);
listStructOctetStringData[index].fabricIndex = entry.fabricIndex;
listStructOctetStringData[index].operationalCert =
ByteSpan(Uint8::from_char(gListOperationalCert[index]), strlen(gListOperationalCert[index]));
index++;
}

if (iter.GetStatus() != CHIP_NO_ERROR)
{
return CHIP_ERROR_INVALID_DATA_LIST;
}

return CHIP_NO_ERROR;
}

CHIP_ERROR TestAttrAccess::ReadListNullablesAndOptionalsStructAttribute(AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([](const TagBoundEncoder & encoder) -> CHIP_ERROR {
Expand All @@ -237,6 +333,11 @@ CHIP_ERROR TestAttrAccess::ReadListNullablesAndOptionalsStructAttribute(Attribut
});
}

CHIP_ERROR TestAttrAccess::WrtieListNullablesAndOptionalsStructAttribute(TLV::TLVReader & aReader)
{
// TODO Add yaml test case for NullablesAndOptionalsStruct list
return CHIP_NO_ERROR;
}
} // namespace

bool emberAfTestClusterClusterTestCallback(app::CommandHandler *, const app::ConcreteCommandPath & commandPath,
Expand Down
30 changes: 0 additions & 30 deletions src/app/tests/suites/TestCluster.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -720,36 +720,6 @@ tests:
arguments:
value: ""

# Tests for List attribute

- label: "Read attribute LIST"
command: "readAttribute"
attribute: "list_int8u"
response:
value: [1, 2, 3, 4]

# Tests for List Octet String attribute

- label: "Read attribute LIST_OCTET_STRING"
command: "readAttribute"
attribute: "list_octet_string"
response:
value: ["Test0", "Test1", "Test2", "Test3"]

# Tests for List Struct Octet String attribute

- label: "Read attribute LIST_STRUCT_OCTET_STRING"
command: "readAttribute"
attribute: "list_struct_octet_string"
response:
value:
[
{ fabricIndex: 0, operationalCert: "Test0" },
{ fabricIndex: 1, operationalCert: "Test1" },
{ fabricIndex: 2, operationalCert: "Test2" },
{ fabricIndex: 3, operationalCert: "Test3" },
]

# Tests for Epoch Microseconds

- label: "Read attribute EPOCH_US Default Value"
Expand Down
Loading

0 comments on commit 8e62ee4

Please sign in to comment.