-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Basic data model structure #1468
Conversation
d649618
to
d8c66f4
Compare
f2b34a7
to
64f4ec9
Compare
Also some additional cleanups
This saves us from having to decide when to free it
For now use CHIP_ERROR_INTERNAL for all errors
* move from arrays to deque, no new or delete (yay!) * move GetValue() to be a member function of clusterserver * EndPoint -> Endpoint (endpoint is one word)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a bunch of comments around the approach in the CL.
Overall, while I like the direction of application-centric ownership of data, I'm very concerned about the intermix of const and non-const data, as well as the memory cost of the architecture. It's fairly heavy, and I don't think will scale well to more complicated data types.
* @brief | ||
* This class implements the Base cluster as defined in the CHIP specification. | ||
*/ | ||
class ClusterBasic : public Cluster |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The class hierarchy designed here (ClusterBasic -> Cluster, which contains Attributes -> Values) is I think much too OO-ified to be performant on embedded targets.
There should be a separation between the specification for a cluster (the set of attributes it has, the type of each attribute, their ranges, etc), from the actual data backing that cluster. The former is typically const type information, since the definition for what a cluster is is immutable, lending itself as well to code-generation. The actual dynamic data backing each attribute is controlled by the application however, and is in RAM.
Here in this CL, the application developer is forced to define clusters by defining themselves what composes that cluster (making it error-prone), as well as placing a lot of pressure on RAM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a bunch of comments around the approach in the CL.
Overall, while I like the direction of application-centric ownership of data, I'm very concerned about the intermix of const and non-const data, as well as the memory cost of the architecture. It's fairly heavy, and I don't think will scale well to more complicated data types.
const can be pulled out with cluster/attribute/value-specific classes, basically putting them in code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I follow - could you provide an example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g. for clusterid, instead of a member variable, a class-static const
class Cluster {
...
virtual ClusterId Id() const = 0;
};
class LevelControl : public Cluster {
static const ClusterId kId = 0x0008; // from spec
virtual ClusterId Id() const { return kId; }
...
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we'd have to be more clever with the structure of the attributes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That approach starts to break down fairly quickly for other const information - e.g the list of attributes in a given cluster. This is represented by themAttrs
private member in the Cluster class, but that clearly cannot be made const.
The only scalable approach IMHO is to actually separately represent the schema-derived information as either code, or better yet, tabular data that is then passed in as an argument to a concrete, mutable, non-const cluster data object.
This is the approach already in place in the existing Silabs code-base - why are we altering it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rwalker-apple I think your example will not actually save any code -- last time I ran into examples like the above there needed to be a final
keyword on the LevelControl::Id
method for the compiler to actually collapse and inline the OO structures. Without the final
I believe in most invocations a call to Id
will actually involve a virtual call, etc (there may be some cases where compiler can prove to itself that LevelControl
is only a LevelControl
and not a subclass, but those are incredibly infrequent). And if you are able to specify final
, it often means that the class hierarchy is not as deep or does not provide as much utility as one had expected.
This pattern looks great as a design principle; in products we've shipped, we have had better success with other structures (e.g. along the lines of what @mrjerryjohns was getting at). And I have spent more time time than I care to admit ripping out interfaces / pure virtual classes from a codebase to squeeze the code into available space.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That approach starts to break down fairly quickly for other const information - e.g the list of attributes in a given cluster. This is represented by the
mAttrs
private member in the Cluster class, but that clearly cannot be made const.The only scalable approach IMHO is to actually separately represent the schema-derived information as either code, or better yet, tabular data that is then passed in as an argument to a concrete, mutable, non-const cluster data object.
This is the approach already in place in the existing Silabs code-base - why are we altering it?
In my view, this experiment is aimed at making the SiLabs ZCL implementations easier to compose and unit test and to be able to provide the implementations to customers as libraries instead of source. The aim is more runtime conditionals/composition, less compile-time conditionals/composition.
SiLabs ZCL has no provision for dynamic configuration of a device (except spawning another process), which feels weird to me, but I'm new to this arena.
{ | ||
public: | ||
ValueTypes mType; | ||
union |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This value union'ing is RAM in-efficient - every single Value instance is going to have the size of the largest item in this list, even though they may not be using it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Regardless of any command mode we consider (only through cluster-command like ZCL or direct attribute updates like HomeKit), this isn't mandatorily required to be part of the DataModel, we only needed the Set/Get APIs for the attributes read/write. The union is only a placeholder for holding values received by the network-stack.
So the union isn't part of the Attribute/Data-Model any more.
kCHIPValueType_EndOfContainer, | ||
}; | ||
|
||
class Value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this approach scales to handle container/collection types, which can nest other simple value types.
Examples include an array of values, or a struct that contains values.
src/lib/datamodel/Attribute.h
Outdated
public: | ||
uint16_t mAttrId; | ||
Value mValue; | ||
Value mMin; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Attributes that don't have a concept of min/max, or don't even specify such a constraint in schema will pay the cost of storage of these fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, these only will show up for Attributes that are range-bound
My initial idea behind exposing the attributes and values (which primarily adds to the data model) is direct access to read/write of these. |
That is definitely not true. Most reads will happen through the ReadAttribute or likely, the Reporting command. Most writes will happen WriteAttribute commands. In-direct mutations through command invocations is actually the rarity here. |
@mrjerryjohns Is that the case for ZCL, or more the case for Weave's data model? When I consider the business logic/attribute dependencies behind LevelControl and OnOff, it seems like command-mutations drive other mutations. Perhaps looking at those 2 clusters first are giving us the wrong impression. |
bd120a7
to
08ac64a
Compare
08ac64a
to
41fe827
Compare
508ea0e
to
f42189d
Compare
Size increase report for "gn_nrf-example-build"
Full report output
|
Size increase report for "gn_linux-example-build"
Full report output
|
Size increase report for "nrf-example-build"
Full report output
|
Size increase report for "linux-example-build"
Full report output
|
Size increase report for "esp32-example-build"
Full report output
|
* | ||
* @param value the new value that this attribute should be updated with | ||
*/ | ||
virtual CHIP_ERROR Set(const Value & newValue) = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means a vtable pointer for every attribute, as well as extra vtables for each subclass (2 so far).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also type-erased, with Value
hiding the actual type of the underlying value element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With Value being still a union, it means that the caller is still going to pay the penalty of maximal storage when utilizing this API.
This API is no better than just having Set(void *value)
- the union inside Value
is effectively an opaque data store. In fact, the latter is better since the caller-side allocated storage for the value is exactly as big as the type of the value they're trying to set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
The other thing to note is that for primitive values that fit in a single register passing them by value produces slightly smaller code than the const reference.
server.AddCluster(switch2); | ||
|
||
/* Validate attributes of the OnOff Cluster on Endpoint 1 */ | ||
server.GetValue(1, kClusterIdOnOff, kAttributeIdOnOff, value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a lot of type-erasure happening here, making for some heavy/clunky APIs.
I want to just be able to do the following as simple as this:
bool value;
server.GetValue(1, kClusterIdOnOff, kAttributeIdOnOff, value);
The validation of the type being boolean should happen as part of GetValue
.
It's fairly true for ZCL. Take a look at the Basic cluster, or the Battery Settings cluster (under Power Configuration). Both have 15+ attributes each, with a lot being R/W that don't have custom setters/getter commands on those clusters to read/write those. |
] | ||
|
||
public_deps = [ | ||
"${chip_root}/src/inet/tests:tests_common", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this dependency needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would echo @mrjerryjohns concerns about extensive use of polymorphism and is impact on RAM and flash usage.
Based on the discussions in the chat channels, closing this for now, until we address the concerns listed there through a proposal first. |
Merge in WMN_TOOLS/matter from remove_rps_creation_ci_step to silabs Squashed commit of the following: commit a5167f7ef98d6fa9b9c8813c1feeafb91f1c16d6 Author: Junior Martinez <[email protected]> Date: Mon Jan 8 13:55:45 2024 -0500 Base on discussion, 1 stash command including both s37 and rps file should work and is cleaner commit 2d35889116430c3588834286b3c488611a403378 Author: Junior Martinez <[email protected]> Date: Mon Jan 8 13:30:00 2024 -0500 Fix rps copy condition commit cd213485b39ad19a192e566d5e5a4ad21c6c4dcd Author: Junior Martinez <[email protected]> Date: Mon Jan 8 12:45:15 2024 -0500 remove Generate RPS stage. Store .rps file after build script for 917 soc. Remove the now unsupported wifi SOC boards from wifi test suites and iot reports
Merge in WMN_TOOLS/matter from remove_rps_creation_ci_step to silabs Squashed commit of the following: commit a5167f7ef98d6fa9b9c8813c1feeafb91f1c16d6 Author: Junior Martinez <[email protected]> Date: Mon Jan 8 13:55:45 2024 -0500 Base on discussion, 1 stash command including both s37 and rps file should work and is cleaner commit 2d35889116430c3588834286b3c488611a403378 Author: Junior Martinez <[email protected]> Date: Mon Jan 8 13:30:00 2024 -0500 Fix rps copy condition commit cd213485b39ad19a192e566d5e5a4ad21c6c4dcd Author: Junior Martinez <[email protected]> Date: Mon Jan 8 12:45:15 2024 -0500 remove Generate RPS stage. Store .rps file after build script for 917 soc. Remove the now unsupported wifi SOC boards from wifi test suites and iot reports
Merge in WMN_TOOLS/matter from remove_rps_creation_ci_step to silabs Squashed commit of the following: commit a5167f7ef98d6fa9b9c8813c1feeafb91f1c16d6 Author: Junior Martinez <[email protected]> Date: Mon Jan 8 13:55:45 2024 -0500 Base on discussion, 1 stash command including both s37 and rps file should work and is cleaner commit 2d35889116430c3588834286b3c488611a403378 Author: Junior Martinez <[email protected]> Date: Mon Jan 8 13:30:00 2024 -0500 Fix rps copy condition commit cd213485b39ad19a192e566d5e5a4ad21c6c4dcd Author: Junior Martinez <[email protected]> Date: Mon Jan 8 12:45:15 2024 -0500 remove Generate RPS stage. Store .rps file after build script for 917 soc. Remove the now unsupported wifi SOC boards from wifi test suites and iot reports
Merge in WMN_TOOLS/matter from remove_rps_creation_ci_step to silabs Squashed commit of the following: commit a5167f7ef98d6fa9b9c8813c1feeafb91f1c16d6 Author: Junior Martinez <[email protected]> Date: Mon Jan 8 13:55:45 2024 -0500 Base on discussion, 1 stash command including both s37 and rps file should work and is cleaner commit 2d35889116430c3588834286b3c488611a403378 Author: Junior Martinez <[email protected]> Date: Mon Jan 8 13:30:00 2024 -0500 Fix rps copy condition commit cd213485b39ad19a192e566d5e5a4ad21c6c4dcd Author: Junior Martinez <[email protected]> Date: Mon Jan 8 12:45:15 2024 -0500 remove Generate RPS stage. Store .rps file after build script for 917 soc. Remove the now unsupported wifi SOC boards from wifi test suites and iot reports
Problem
Define basic objects for Cluster Server, EndPoint, Cluster and Attributes. Use it from the application. Only a small subset of cluster/attributes is implemented for feedback. The changes to the
wifi-echo
example provide a quick overview.CC @bhaskar-apple
So currently with the following call in our echo server:
the following database is created:
and the function
is executed when the attribute is updated over the network
Summary of Changes
Created a structure so that the applications can express the cluster definitions with minimum hassle. The names will be updated to whatever are finalised by the TT. Purposefully there are no 'private' members right now. Other dataTypes, clusters and mandatory attributes within clusters can be defined in subsequent requests.