@@ -7,195 +7,193 @@ general overview of the code-driven cluster architecture.
77
88---
99
10- ## Step 1: Evaluate the Existing Ember Implementation
11-
12- Before writing new code, it's crucial to understand how the current Ember
13- cluster is implemented. Ember clusters typically use a mix of the following
14- patterns:
15-
16- ### Pure Ember Implementation (Simple Attributes)
17-
18- For simple attributes, the Ember framework in ` src/app/util ` handles data
19- storage and access. The application interacts with these attributes via the
20- type-safe accessors in
21- ` zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h ` .
22-
23- In a code-driven implementation, this data must be moved into member variables
24- within your new cluster class.
25-
26- In some cases the cluster directory may not exist. In that case, create a new
27- directory and add the mapping in
28- [ src/app/zap_cluster_list.json] ( https://github.com/project-chip/connectedhomeip/blob/master/src/app/zap_cluster_list.json )
29- under the ` ServerDirectories ` key.
30-
31- ### ` AttributeAccessInterface ` (` AAI ` ) and ` CommandHandlerInterface ` (` CHI ` )
32-
33- When more complex logic is needed, Ember clusters use these interfaces.
34-
35- - ` AAI ` can be directly translated to the ` ReadAttribute ` and ` WriteAttribute `
36- methods in your new cluster class.
37- - ` CHI ` can be translated to the ` InvokeCommand ` method.
38-
39- ### Determine ember/zap storage
40-
41- Ember storage should be moved from ` persist/ram/callback ` into ` ram/callback ` :
42-
43- - if the value loaded from ZAP UI needs to be loaded, use ` RAM ` and have
44- ` CodegenIntegration.cpp ` load the value from ZAP via ` Accessors.h ` . A common
45- example here is ` FeatureMap `
46-
47- - if the value is internal to the cluster or does not need loading, set it as
48- ` External ` by including it in the ` attributeAccessInterfaceAttributes ` in
49- ` zcl.json ` and ` zcl-with-test-extensions.json ` . A common example here is
50- ` ClusterRevision `
51-
52- - if the value used to be ` persist ` it is an indication that the cluster
53- should handle persistence (load in ` Startup ` and store during writes).
54-
55- ## Step 2: Design the Code-Driven Implementation
56-
57- ### Class Layout and File Structure
58-
59- When converting clusters, optimizing for flash usage is often a priority. For
60- this reason, it's common to implement the cluster without separating the logic
61- from the implementation class.
62-
63- - ** Recommendation:** Combine logic and data storage into a single
64- ` <Name>Cluster ` class that implements the ` ServerClusterInterface ` .
65- - ** Trade-off:** This approach prioritizes a smaller flash footprint over the
66- modular, more testable layout described in the
67- [ Writing Clusters] ( ./writing_clusters.md ) guide. The combined approach is
68- often suitable for simpler clusters or when resource constraints are tight.
69-
70- You will need to create the following files:
71-
72- - ` src/app/clusters/<name>/<Name>Cluster.h `
73- - ` src/app/clusters/<name>/<Name>Cluster.cpp `
74- - ` src/app/clusters/<name>/CodegenIntegration.cpp `
75- - ` src/app/clusters/<name>/tests/Test<Name>Cluster.cpp `
76- - Build files (` BUILD.gn ` , ` tests/BUILD.gn ` ,
77- ` app_config_dependent_sources.gni ` , ` app_config_dependent_sources.cmake ` )
78-
79- ### Attribute and Command Availability
80-
81- Your new class must accurately report which attributes and commands are
82- available based on the feature map and other configuration.
83-
84- - Store the feature map in your cluster instance.
85- - For optional attributes, use a ` BitFlags ` variable, an
86- ` OptionalAttributeSet ` , or a ` struct ` of booleans to track which are
87- enabled.
88-
89- ---
90-
91- ## Step 3: Implement the Cluster Logic
92-
93- This step involves translating the logic from the old Ember patterns into the
94- new code-driven class structure.
95-
96- #### Attribute Storage and Persistence
97-
98- - Attributes are now member variables of your cluster class.
99- - If attributes were configured as ` RAM ` in ` zap ` to load defaults, ensure
100- your ` CodegenIntegration.cpp ` reads these defaults and passes them to your
101- cluster's constructor.
102- - For persisted attributes, use the ` AttributePersistence ` helper, which is
103- available via the ` ServerClusterContext ` . Load values in ` Startup ` and save
104- them on writes.
105-
106- #### Command Handling
107-
108- - Translate ` CommandHandlerInterface ` calls or ` emberAf...Callback ` functions
109- into logic inside your ` InvokeCommand ` method:
110-
111- - ember calls of the form ` emberAf<CLUSTER>Cluster<COMMAND>Callback ` will
112- be converted to a switch ` case <CLUSTER>::Commands::<COMMAND>::Id: ... `
113- implementation
114-
115- Example:
116-
117- ``` cpp
118- // This
119- emberAfAccessControlClusterReviewFabricRestrictionsCallback (...);
120-
121- // Becomes this in the `InvokeCommand` implementation:
122- switch (request.path.mCommandId) {
123- // ...
124- case AccessControl::Commands::ReviewFabricRestrictions::Id:
125- // ...
126- }
10+ ## Migration Checklist
11+
12+ This checklist provides a granular, step-by-step process for migrating an Ember
13+ cluster to a code-driven implementation.
14+
15+ ### Part 0: Optimizing for an Easier Review
16+
17+ Before you begin the migration, consider structuring your changes into multiple,
18+ smaller pull requests. This approach significantly simplifies the review
19+ process, allowing reviewers to approve preliminary changes quickly. We recommend
20+ the following sequence of PRs:
21+
22+ - [ ] ** PR 1: File Renames Only.**
23+
24+ - If your migration involves renaming files, submit a PR containing _ only_
25+ the renames. A typical rename is from ` <name>-server.cpp ` to
26+ ` <Name>Cluster.cpp ` .
27+ - ** Note:** For backward compatibility with code generation, it is often
28+ best to ** not** rename the header file.
29+ - ** Why:** This prevents ` git diff ` from becoming confused and showing the
30+ entire file as deleted and recreated, making the actual code changes
31+ impossible to review.
32+ - _ This type of PR can be reviewed and merged very quickly._
33+
34+ - [ ] ** PR 2: Code Movement Only.**
35+
36+ - If you plan to reorder functions or move code blocks (e.g., moving
37+ helper functions to an anonymous namespace), submit a PR with _ only_
38+ these movements. Do not change any logic.
39+ - ** Why:** Reviewers can use tools like ` git diff --color-moved ` to verify
40+ that code has only been moved, not altered. This allows for a rapid
41+ review of structural changes.
42+ - _ This type of PR can also be fast-tracked._
43+
44+ - [ ] ** PR 3: The Core Logic Changes.**
45+ - This PR should contain the actual migration logic: implementing the new
46+ cluster class, moving attribute storage, and converting command
47+ handlers.
48+ - ** Why:** With renames and code movements already handled, this PR will
49+ be much smaller and focused, allowing the reviewer to concentrate solely
50+ on the correctness of the migration logic.
51+
52+ This structure respects the reviewer's time and helps get your changes merged
53+ faster. You can ask for an expedited review of the preliminary PRs in the
54+ project's Slack channel.
55+
56+ ### Part 1: Analysis and Design
57+
58+ - [ ] ** 1.1: Understand the Existing Implementation:**
59+
60+ - [ ] Identify if the cluster uses a pure Ember implementation,
61+ ` AttributeAccessInterface ` (` AAI ` ), or ` CommandHandlerInterface `
62+ (` CHI ` ).
63+ - ** Pure Ember Implementation:** For simple attributes, the Ember
64+ framework in ` src/app/util ` handles data storage and access. In a
65+ code-driven implementation, this data ** must be moved into member
66+ variables** within your new cluster class.
67+ - ** AAI and CHI:** For more complex logic, ` AAI ` translates to the
68+ ` ReadAttribute ` and ` WriteAttribute ` methods, while ` CHI ` translates
69+ to the ` InvokeCommand ` method.
70+ - [ ] Determine how attributes are stored (e.g., ` persist ` , ` ram ` ,
71+ ` callback ` ). This will inform how you handle data in the new
72+ implementation.
73+ - If the value was ` persist ` , the new cluster should handle
74+ persistence (load in ` Startup ` and store during writes).
75+ - If the value was ` ram ` and loaded from the ZAP UI,
76+ ` CodegenIntegration.cpp ` should load the value from ZAP via the
77+ generated ` Accessors.h ` . A common example is the ` FeatureMap ` .
78+ - If the value is internal to the cluster (e.g., ` ClusterRevision ` ),
79+ it should be marked as ` External ` by adding it to
80+ ` attributeAccessInterfaceAttributes ` in ` zcl.json ` .
81+
82+ - [ ] ** 1.2: Choose an Implementation Pattern:**
83+
84+ - [ ] Decide between a combined or modular implementation based on the
85+ cluster's complexity and resource constraints. See the
86+ [ Writing Clusters] ( ./writing_clusters.md#choosing-the-right-implementation-pattern )
87+ guide for more details.
88+
89+ - [ ] ** 1.3: Create the File Structure:**
90+ - [ ] Use an existing directory for the cluster or create a new one if
91+ missing at ` src/app/clusters/<name>/ ` .
92+ - ** Note:** If the cluster was a pure Ember implementation, this
93+ directory may not exist. After creating it, you must add a mapping
94+ to ` src/app/zap_cluster_list.json ` under the ` ServerDirectories `
95+ key.
96+ - [ ] Add the following files:
97+ - ` <Name>Cluster.h `
98+ - ` <Name>Cluster.cpp `
99+ - ` CodegenIntegration.cpp `
100+ - ` tests/Test<Name>Cluster.cpp `
101+ - ` BUILD.gn `
102+ - ` tests/BUILD.gn `
103+ - ` app_config_dependent_sources.gni `
104+ - ` app_config_dependent_sources.cmake `
105+
106+ ### Part 2: Implementation
107+
108+ - [ ] ** 2.1: Implement the Cluster Class:**
109+
110+ - [ ] Define the ` <Name>Cluster ` class, inheriting from
111+ ` DefaultServerCluster ` .
112+ - [ ] Add member variables for all attributes previously handled by Ember.
113+ - [ ] Implement the ` Startup ` and ` Shutdown ` methods for resource
114+ management.
115+
116+ - [ ] ** 2.2: Implement Attribute Logic:**
117+
118+ - [ ] Implement the ` ReadAttribute ` and ` WriteAttribute ` methods,
119+ translating any existing ` AAI ` logic.
120+ - [ ] For persisted attributes, use the ` AttributePersistence ` helper to
121+ load in ` Startup ` and save on writes.
122+ - [ ] ** Crucially,** after a successful write, call a notification
123+ function (e.g., ` NotifyAttributeChangedIfSuccess ` ) to ensure
124+ subscriptions work correctly.
125+
126+ - [ ] ** 2.3: Implement Command Logic:**
127+
128+ - [ ] Implement the ` InvokeCommand ` method, translating any existing ` CHI `
129+ or ` emberAf...Callback ` logic.
130+
131+ - ** Example:** An ` emberAf<CLUSTER>Cluster<COMMAND>Callback ` function
132+ becomes a ` case ` in the ` InvokeCommand ` 's ` switch ` statement:
133+
134+ ``` cpp
135+ // This:
136+ emberAfAccessControlClusterReviewFabricRestrictionsCallback (...);
137+
138+ // Becomes this in the `InvokeCommand` implementation:
139+ switch (request.path.mCommandId) {
140+ // ...
141+ case AccessControl::Commands::ReviewFabricRestrictions::Id:
142+ // ...
143+ }
144+ ```
145+
146+ - [ ] Use a `switch` statement on the command ID to handle different
147+ commands.
148+
149+ - [ ] ** 2.4: Implement Event Logic:**
150+ - [ ] Replace any calls to ` LogEvent ` with
151+ ` mContext->interactionContext.eventsGenerator.GenerateEvent ` to make
152+ events unit-testable.
153+ - [ ] Check if any events have non-default access permissions (e.g.,
154+ require Administrator access) and implement the ` EventInfo ` method
155+ if necessary.
156+
157+ ### Part 3: Configuration and Integration
158+
159+ - [ ] ** 3.1: Update Build Files:**
160+
161+ - [ ] Configure ` BUILD.gn ` and ` tests/BUILD.gn ` to include the new source
162+ files.
163+ - [ ] Configure ` app_config_dependent_sources.gni ` and
164+ ` app_config_dependent_sources.cmake ` to integrate the cluster into
165+ the build system.
166+
167+ - [ ] ** 3.2: Implement Codegen Integration:**
168+
169+ - [ ] In ` CodegenIntegration.cpp ` , use the ` CodegenClusterIntegration `
170+ helper to read configuration values from the generated code.
171+ - > ** Note:** The ` CodegenClusterIntegration ` helper for optional
172+ > attributes only supports attribute IDs up to 31. For clusters with
173+ > higher attribute IDs, you will need a custom implementation.
174+
175+ - [ ] ** 3.3: Update ZAP Configuration:**
176+ - [ ] In ` src/app/common/templates/config-data.yaml ` , add the cluster to
177+ the ` CommandHandlerInterfaceOnlyClusters ` array.
178+ - [ ] In ` src/app/zap-templates/zcl/zcl.json ` and
179+ ` zcl-with-test-extensions.json ` , add all non-list attributes to the
180+ ` attributeAccessInterfaceAttributes ` list.
181+ - [ ] Run the ZAP regeneration script:
182+ ``` bash
183+ ./scripts/run_in_build_env.sh ' scripts/tools/zap_regen_all.py'
127184 ```
128185
129- - Command Handler Interface logic translates directly: `CHI` has a switch
130- on command ID inside its `InvokeCommand` call. You should have the same
131- logic inside the `ServerClusterInterface` processing logic.
132-
133- - The ` InvokeCommand ` method can return an ` ActionReturnStatus ` optional. For
134- better readability, prefer returning a status code directly (e.g.,
135- ` return Status::Success; ` ) rather than using the command handler to set the
136- status, unless you need to return a response with a value or handle the
137- command asynchronously.
138-
139- #### Attribute Access
140-
141- - Translate ` AAI ` logic into your ` ReadAttribute ` and ` WriteAttribute `
142- methods.
143- - ** Important:** After successfully writing a new attribute value, you
144- ** must** explicitly call a notification function (e.g., via
145- ` interactionContext->dataModelChangeListener ` ) to inform the SDK of the
146- change. This is required for subscriptions to work correctly.
147-
148- #### Event Generation
149-
150- - Replace any calls to ` LogEvent ` with
151- ` mContext->interactionContext.eventsGenerator.GenerateEvent ` . This makes
152- events unit-testable.
186+ # ## Part 4: Testing
153187
154- ---
155-
156- ## Step 4: Update Build and Codegen Configuration
157-
158- #### Build Files
159-
160- Create the necessary build files (` BUILD.gn ` , ` tests/BUILD.gn ` ,
161- ` app_config_dependent_sources.gni ` , ` app_config_dependent_sources.cmake ` ) to
162- integrate your new cluster files into the build system.
163-
164- #### Codegen Integration
165-
166- In ` CodegenIntegration.cpp ` , use the ` CodegenClusterIntegration ` helper class to
167- minimize the boilerplate needed to read configuration values (like feature maps
168- and optional attribute lists) from the generated code.
169-
170- > ** Note:** The ` CodegenClusterIntegration ` helper for optional attributes only
171- > supports attribute IDs up to 31. For clusters with higher attribute IDs (e.g.,
172- > ` On/Off ` , ` Color Control ` ), you will need a custom implementation.
173-
174- #### ZAP Configuration
175-
176- You must update the ZAP configuration to inform the code generator that your
177- cluster is now code-driven. This prevents the Ember framework from managing its
178- data.
179-
180- 1 . ** Update ` config-data.yaml ` :** In
181- ` src/app/common/templates/config-data.yaml ` , add your cluster to the
182- ` CommandHandlerInterfaceOnlyClusters ` array. This disables Ember's command
183- dispatch for this cluster.
184- 2 . ** Update ` zcl.json ` :** In ` src/app/zap-templates/zcl/zcl.json ` and
185- ` zcl-with-test-extensions.json ` , add all of your cluster's non-list
186- attributes to the ` attributeAccessInterfaceAttributes ` list. This tells ZAP
187- not to allocate RAM for these attributes, as your class now manages them.
188- 3 . Re-run ZAP regeneration, like
189-
190- ``` bash
191- ./scripts/run_in_build_env.sh ' scripts/tools/zap_regen_all.py'
192- ```
193-
194- ---
188+ - [ ] ** 4.1: Write Unit Tests:**
195189
196- # # Step 5: Add Unit Tests
190+ - [ ] In ` tests/Test< Name> Cluster.cpp` , add unit tests for the new
191+ implementation.
192+ - [ ] Ensure all attributes, commands, and feature combinations are
193+ tested.
197194
198- - Create ` tests/Test< Name> Cluster.cpp` and add unit tests for your new
199- implementation.
200- - Ensure you test the cluster' s logic with various feature combinations and
201- for all supported attributes and commands.
195+ - [ ] ** 4.2: Perform Integration Testing:**
196+ - [ ] Integrate the cluster into an example application (e.g.,
197+ ` all-clusters-app` ).
198+ - [ ] Manually validate the cluster' s functionality using `chip-tool` or
199+ `matter-repl`.
0 commit comments