Skip to content
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

Closed
wants to merge 36 commits into from

Conversation

kedars
Copy link
Contributor

@kedars kedars commented Jul 7, 2020

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:

    ClusterServer server(applicationVersion,
                             HWVersion);
    /* Add a cluster to the primary endpoint of our cluster server */
    server.AddCluster(LEDWidget()); //class LEDWidget() derives from class ClusterOnOff

the following database is created:

Server:
  EndPoint: 1
    ClusterId: 0x0000 (Base)
      Attribute: 0x0000 (ZCLVersion)
              Value: 10
      Attribute: 0x0001 (ApplicationVersion)
              Value: 11
      Attribute: 0x0002 (StackVersion)
              Value: 12
      Attribute: 0x0003 (HWVersion)
              Value: 13
    ClusterId: 0x0006 (OnOff)
      Attribute: 0x0000 (On/Off)
              Value: false
      Attribute: 0x4000 (Global Scene Control)
              Value: false
      Attribute: 0x4001 (OnTime)
              Value: 0
      Attribute: 0x4002 (OffWaitTime)
              Value: 0

and the function

int LEDWidget::Set(AttributeId_t attrId, const CHIPValue &value)

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.

src/lib/core/CHIPEndPoint.h Outdated Show resolved Hide resolved
src/lib/core/CHIPEndPoint.h Outdated Show resolved Hide resolved
src/lib/core/CHIPEndPoint.h Outdated Show resolved Hide resolved
src/lib/core/CHIPStandardAttributes.h Outdated Show resolved Hide resolved
src/lib/core/CoreLayer.am Outdated Show resolved Hide resolved
@CLAassistant
Copy link

CLAassistant commented Jul 7, 2020

CLA assistant check
All committers have signed the CLA.

@kedars kedars force-pushed the basic_data_model_structure branch from d649618 to d8c66f4 Compare July 10, 2020 09:30
@kedars kedars force-pushed the basic_data_model_structure branch from f2b34a7 to 64f4ec9 Compare July 10, 2020 10:19
Copy link
Contributor

@mrjerryjohns mrjerryjohns left a 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
Copy link
Contributor

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.

Copy link
Contributor

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?

Copy link
Contributor

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?

Copy link
Contributor

@rwalker-apple rwalker-apple Jul 17, 2020

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; }
     ... 
};

Copy link
Contributor

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

Copy link
Contributor

@mrjerryjohns mrjerryjohns Jul 21, 2020

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?

Copy link
Contributor

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.

Copy link
Contributor

@rwalker-apple rwalker-apple Jul 22, 2020

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?

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
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

public:
uint16_t mAttrId;
Value mValue;
Value mMin;
Copy link
Contributor

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.

Copy link
Contributor Author

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

@kedars
Copy link
Contributor Author

kedars commented Jul 16, 2020

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.

My initial idea behind exposing the attributes and values (which primarily adds to the data model) is direct access to read/write of these.
As I later realised, the primary read/write to these will be through cluster-specific commands. In such a case, creating a base-class for attributes and values, may not be warranted. We could embed those within the cluster itself, thus optimising the corresponding .data bits (pointers & enum), while the mix/max be encoded in the code as rwalker point above.

@mrjerryjohns
Copy link
Contributor

the primary read/write to these will be through cluster-specific commands

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.

@rwalker-apple
Copy link
Contributor

rwalker-apple commented Jul 17, 2020

the primary read/write to these will be through cluster-specific commands

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.

@kedars kedars force-pushed the basic_data_model_structure branch 2 times, most recently from bd120a7 to 08ac64a Compare July 19, 2020 11:15
@kedars kedars force-pushed the basic_data_model_structure branch from 08ac64a to 41fe827 Compare July 19, 2020 11:20
@kedars kedars force-pushed the basic_data_model_structure branch from 508ea0e to f42189d Compare July 21, 2020 09:00
@github-actions
Copy link

Size increase report for "gn_nrf-example-build"

File Section File VM
Full report output
BLOAT REPORT

Files found only in the build output:
    report.csv


@github-actions
Copy link

Size increase report for "gn_linux-example-build"

File Section File VM
Full report output
BLOAT REPORT

Files found only in the build output:
    report.csv


@github-actions
Copy link

Size increase report for "nrf-example-build"

File Section File VM
Full report output
BLOAT REPORT

Files found only in the build output:
    report.csv

Comparing ./master_artifact/chip-nrf52840-lock-example.out and ./pull_artifact/chip-nrf52840-lock-example.out:

sections,vmsize,filesize


@github-actions
Copy link

Size increase report for "linux-example-build"

File Section File VM
Full report output
BLOAT REPORT

Files found only in the build output:
    report.csv

Comparing ./master_artifact/chip-standalone-demo.out and ./pull_artifact/chip-standalone-demo.out:

sections,vmsize,filesize


@github-actions
Copy link

Size increase report for "esp32-example-build"

File Section File VM
chip-wifi-echo.elf .flash.text 1576 1576
chip-wifi-echo.elf .dram0.bss 0 240
chip-wifi-echo.elf .flash.rodata 120 120
Full report output
BLOAT REPORT

Files found only in the build output:
    report.csv

Comparing ./master_artifact/chip-wifi-echo.elf and ./pull_artifact/chip-wifi-echo.elf:

sections,vmsize,filesize
.debug_info,0,40392
.debug_line,0,9029
.debug_str,0,8879
.debug_loc,0,3734
.debug_ranges,0,2672
.debug_abbrev,0,2379
[41 Others],0,2372
.shstrtab,0,2210
.flash.text,1576,1576
.strtab,0,1461
.symtab,0,1376
.debug_frame,0,816
.debug_aranges,0,272
.dram0.bss,240,0
.xt.prop._ZN9LEDWidgetD2Ev,0,156
.xt.prop._ZN4chip9DataModel7Cluster3SetEtRKNS0_5ValueE,0,136
.xt.prop._ZN4chip9DataModel7Cluster3GetEtRNS0_5ValueE,0,124
.flash.rodata,120,120
.xt.lit._ZN6Screen8GetTitleB5cxx11Ev,0,120
.xt.lit._ZN4chip9DataModel7ClusterD5Ev,0,88
[Unmapped],0,-120


*
* @param value the new value that this attribute should be updated with
*/
virtual CHIP_ERROR Set(const Value & newValue) = 0;
Copy link
Contributor

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).

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

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);
Copy link
Contributor

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.

@mrjerryjohns
Copy link
Contributor

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

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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this dependency needed?

Copy link
Contributor

@robszewczyk robszewczyk left a 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.

@kedars
Copy link
Contributor Author

kedars commented Jul 27, 2020

Based on the discussions in the chat channels, closing this for now, until we address the concerns listed there through a proposal first.

@kedars kedars closed this Jul 27, 2020
shgutte pushed a commit to shgutte/connectedhomeip that referenced this pull request Jan 11, 2024
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
rcasallas-silabs pushed a commit to rcasallas-silabs/connectedhomeip that referenced this pull request Jun 20, 2024
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
rcasallas-silabs pushed a commit to rcasallas-silabs/connectedhomeip that referenced this pull request Jun 20, 2024
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
chirag-silabs pushed a commit to rosahay-silabs/connectedhomeip that referenced this pull request Jul 15, 2024
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.