Skip to content

Commit

Permalink
Add commands support for dynamically generated clusters in dynamic-br…
Browse files Browse the repository at this point in the history
…idge-app (#23840)

* Add commands for bridge-generated dynamic clusters

* Allow reads and writes to externally stored OnOff cluster attribute

* dynamic-bridge-app: implement generic read and write callbacks for externally stored attributes

* Update bridge idl tests to include external storage flags

* Restyled by clang-format

* Document the data buffer padding size used when reading/writing external attributes

* Add better error handling for attribute read/write overrides in dynamic-bridge-app

* Allow bitmap32 attribute reads for dynamic clusters in dynamic-bridge-app

* Adjust indentation in BridgeClustersCpp.jinja

* Change the incoming command list for generated clusters to static

* Change the incoming command list for generated clusters to const

* Rename incomingCommandList to mIncomingCommandList for generated clusters in dynamic-bridge-app

* add a test for bridge generated cluster headers

* Restyle one line if statements in dynamic-bridge-app main.cpp

* dynamic-bridge-app: change buffer read logic to assume byte arrays

* Restyled by clang-format

* dynamic-bridge-app: move tests to the proper location

* dynamic-bridge-app: undo formatting done to matter_idl tests restyled by clang-formatter

* dynamic-bridge-app: change read and write functions to use AnonymousTag() instead of Tag()

* dynamic-bridge-app: document unclear code and refactor function and variable names for better readability

* dynamic-bridge-app: mark OnListWriteBegin and OnListWriteEnd as overrides

* dynamic-bridge-app: fix error handling in WriteValueToBuffer

* dynamic-bridge-app: simplify the include path in BridgeClustersCpp jinja template and update the relevant tests

* dynamic-bridge-app: change CHIP_ERROR_BUFFER_TOO_SMALL errors to CHIP_ERROR_INVALID_ARGUMENT

* dynamic-bridge-app: allow read operations on externally stored list attributes

* Restyled by whitespace

* Restyled by clang-format

* dynamic-bridge-app: formatting - add whitespace between function definitions

* dynamic-bridge-app: fix typo in GeneratedClusters.h

Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
2 people authored and pull[bot] committed Feb 13, 2024
1 parent 2439b17 commit 1714833
Show file tree
Hide file tree
Showing 19 changed files with 551 additions and 31 deletions.
17 changes: 9 additions & 8 deletions examples/dynamic-bridge-app/linux/UserInputBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ void AddCluster(const std::vector<std::string> & tokens)
auto c = CreateCluster(cluster_name.c_str());
if (c)
{
g_pending->AddCluster(std::make_unique<DynamicCluster>(std::move(c)));
g_pending->AddCluster(
std::make_unique<DynamicCluster>(std::move(c), c->GetIncomingCommandList(), c->GetOutgoingCommandList()));
}
else
{
Expand Down Expand Up @@ -285,20 +286,20 @@ void ParseValue(std::vector<uint8_t> * data, uint16_t size, const std::string &
{
case ZCL_OCTET_STRING_ATTRIBUTE_TYPE:
case ZCL_CHAR_STRING_ATTRIBUTE_TYPE:
wr.PutString(chip::TLV::Tag(), str.data(), (uint32_t) str.size());
wr.PutString(chip::TLV::AnonymousTag(), str.data(), (uint32_t) str.size());
break;
case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE:
case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE:
wr.PutBytes(chip::TLV::Tag(), (const uint8_t *) str.data(), (uint32_t) str.size());
wr.PutBytes(chip::TLV::AnonymousTag(), (const uint8_t *) str.data(), (uint32_t) str.size());
break;
case ZCL_STRUCT_ATTRIBUTE_TYPE:
// Writing structs not supported yet
break;
case ZCL_SINGLE_ATTRIBUTE_TYPE:
wr.Put(chip::TLV::Tag(), (float) atof(str.c_str()));
wr.Put(chip::TLV::AnonymousTag(), (float) atof(str.c_str()));
break;
case ZCL_DOUBLE_ATTRIBUTE_TYPE:
wr.Put(chip::TLV::Tag(), atof(str.c_str()));
wr.Put(chip::TLV::AnonymousTag(), atof(str.c_str()));
break;
case ZCL_INT8S_ATTRIBUTE_TYPE:
case ZCL_INT16S_ATTRIBUTE_TYPE:
Expand All @@ -308,7 +309,7 @@ void ParseValue(std::vector<uint8_t> * data, uint16_t size, const std::string &
case ZCL_INT48S_ATTRIBUTE_TYPE:
case ZCL_INT56S_ATTRIBUTE_TYPE:
case ZCL_INT64S_ATTRIBUTE_TYPE:
wr.Put(chip::TLV::Tag(), (int64_t) strtoll(str.c_str(), nullptr, 10));
wr.Put(chip::TLV::AnonymousTag(), (int64_t) strtoll(str.c_str(), nullptr, 10));
break;

case ZCL_INT8U_ATTRIBUTE_TYPE:
Expand All @@ -319,12 +320,12 @@ void ParseValue(std::vector<uint8_t> * data, uint16_t size, const std::string &
case ZCL_INT48U_ATTRIBUTE_TYPE:
case ZCL_INT56U_ATTRIBUTE_TYPE:
case ZCL_INT64U_ATTRIBUTE_TYPE:
wr.Put(chip::TLV::Tag(), (uint64_t) strtoll(str.c_str(), nullptr, 10));
wr.Put(chip::TLV::AnonymousTag(), (uint64_t) strtoll(str.c_str(), nullptr, 10));
break;

default:
// Assume integer
wr.Put(chip::TLV::Tag(), (int64_t) strtoll(str.c_str(), nullptr, 10));
wr.Put(chip::TLV::AnonymousTag(), (int64_t) strtoll(str.c_str(), nullptr, 10));
break;
}
wr.Finalize();
Expand Down
12 changes: 12 additions & 0 deletions examples/dynamic-bridge-app/linux/include/GeneratedClusters.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,16 @@ class GeneratedCluster
virtual chip::ClusterId GetClusterId() = 0;
// Gets the list of available attributes for this cluster.
virtual std::vector<AttributeInterface *> GetAttributes() = 0;

// Returns a list of client to server commands. Can be nullptr or terminated by 0xFFFF_FFFF.
// The returned list mirrors the `acceptedCommandList` field in `EmberAfCluster`
// This function is used to pass a command list when creating a `DynamicCluster` and its underlying `EmberAfCluster`. See
// `AddCluster` in `UserInputBackend`.
virtual const chip::CommandId * GetIncomingCommandList() { return nullptr; }

// Returns a list of server generated commands (responses to client commands). Can be nullptr or terminated by 0xFFFF_FFFF.
// The returned list mirrors the `generatedCommandList` field in `EmberAfCluster`
// This function is used to pass a command list when creating a `DynamicCluster` and its underlying `EmberAfCluster`. See
// `AddCluster` in `UserInputBackend`.
virtual const chip::CommandId * GetOutgoingCommandList() { return nullptr; }
};
19 changes: 19 additions & 0 deletions examples/dynamic-bridge-app/linux/include/data-model/Attribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class AttributeInterface

// Read the contents of the attribute.
virtual CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath & aPath, chip::app::AttributeValueEncoder & aEncoder) = 0;

virtual CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath & aPath, chip::TLV::TLVWriter & writer) = 0;
};

// This is the base type for implementing an attribute
Expand All @@ -52,6 +54,23 @@ struct Attribute : public AttributeInterface
return chip::app::DataModel::Encode(aPath, aEncoder, mData);
}

template <typename T = Type, std::enable_if_t<chip::app::DataModel::IsList<std::decay_t<T>>::value, bool> = true>
CHIP_ERROR ReadValue(const chip::app::ConcreteReadAttributePath & aPath, chip::TLV::TLVWriter & writer, Type & value)
{
return chip::app::DataModel::Encode(aPath, writer, chip::TLV::AnonymousTag(), value);
}

template <typename T = Type, std::enable_if_t<!chip::app::DataModel::IsList<std::decay_t<T>>::value, bool> = true>
CHIP_ERROR ReadValue(const chip::app::ConcreteReadAttributePath & aPath, chip::TLV::TLVWriter & writer, Type & value)
{
return chip::app::DataModel::Encode(writer, chip::TLV::AnonymousTag(), value);
}

CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath & aPath, chip::TLV::TLVWriter & writer) override
{
return ReadValue(aPath, writer, mData);
}

void operator=(const Type & value) { mData = value; }
const Type & Peek() const { return mData; }

Expand Down
56 changes: 56 additions & 0 deletions examples/dynamic-bridge-app/linux/include/data-model/DataModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ CHIP_ERROR Encode(const ConcreteReadAttributePath &, AttributeValueEncoder & aEn
/*
* @brief
* Lists that are string-like should be encoded as char/byte spans.
* Encode using a given AttributeValueEncoder.
*/
template <
typename X,
Expand All @@ -74,11 +75,26 @@ CHIP_ERROR Encode(const ConcreteReadAttributePath & aPath, AttributeValueEncoder
return aEncoder.Encode(Span<std::decay_t<typename X::pointer>>(x.data(), x.size()));
}

/*
* @brief
* Lists that are string-like should be encoded as char/byte spans.
* Encode using a given TLVWriter.
*/
template <
typename X,
std::enable_if_t<IsList<std::decay_t<X>>::value && sizeof(std::decay_t<typename X::pointer>) == sizeof(char), bool> = true>
CHIP_ERROR Encode(const ConcreteReadAttributePath & aPath, TLV::TLVWriter & writer, TLV::Tag tag, const X & x)
{
return writer.Put(tag, Span<std::decay_t<typename X::pointer>>(x.data(), x.size()));
}

/*
* @brief
*
* If an item is requested from a list, encode just that single item, or the entire list otherwise.
*
* Encodes items using a given AttributeValueEncoder.
*
* The object must satisfy the following constraints
* size() must return an integer
* begin() must return a type conforming to LegacyRandomAccessIterator
Expand Down Expand Up @@ -113,6 +129,46 @@ CHIP_ERROR Encode(const ConcreteReadAttributePath & aPath, AttributeValueEncoder
});
}

/*
* @brief
*
* If an item is requested from a list, encode just that single item, or the entire list otherwise.
*
* Encodes items using a given TLVWriter.
*
* The object must satisfy the following constraints
* size() must return an integer
* begin() must return a type conforming to LegacyRandomAccessIterator
*
* This is const X& instead of X&& because it is "more specialized" and so this overload will
* be chosen if possible.
*/
template <
typename X,
std::enable_if_t<IsList<std::decay_t<X>>::value && (sizeof(std::decay_t<typename X::pointer>) > sizeof(char)), bool> = true>
CHIP_ERROR Encode(const ConcreteReadAttributePath & aPath, TLV::TLVWriter & writer, TLV::Tag tag, const X & x)
{
if (aPath.mListIndex.HasValue())
{
uint16_t index = aPath.mListIndex.Value();
if (index >= x.size())
return CHIP_ERROR_INVALID_ARGUMENT;

auto it = x.begin();
std::advance(it, index);
return Encode(writer, tag, *it);
}
TLV::TLVType type;

ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Array, type));
for (auto & item : x)
{
ReturnErrorOnFailure(Encode(writer, tag, item));
}
ReturnErrorOnFailure(writer.EndContainer(type));
return CHIP_NO_ERROR;
}

/*
* @brief
* Set of overloaded encode methods that can be called from AttributeAccessInterface::Read
Expand Down
2 changes: 2 additions & 0 deletions examples/dynamic-bridge-app/linux/include/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,5 @@ chip::Span<Action *> GetActionListInfo(chip::EndpointId parentId);
chip::Optional<chip::ClusterId> LookupClusterByName(const char * name);
std::unique_ptr<GeneratedCluster> CreateCluster(const char * name);
std::unique_ptr<GeneratedCluster> CreateCluster(chip::ClusterId id);
EmberAfStatus HandleReadOnOffAttribute(Attribute<bool> * attribute, uint8_t * buffer, uint16_t maxReadLength);
EmberAfStatus HandleWriteOnOffAttribute(Attribute<bool> * attribute, uint8_t * buffer);
Loading

0 comments on commit 1714833

Please sign in to comment.