Line data Source code
1 : /*
2 : * Copyright (c) 2025 Project CHIP Authors
3 : * All rights reserved.
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 : #pragma once
18 :
19 : #include <app/CommandHandlerInterface.h>
20 : #include <app/ConcreteCommandPath.h>
21 : #include <app/data-model-provider/ActionReturnStatus.h>
22 : #include <app/data-model-provider/MetadataTypes.h>
23 : #include <app/data-model-provider/Provider.h>
24 : #include <app/persistence/AttributePersistenceProvider.h>
25 : #include <app/server-cluster/ServerClusterInterface.h>
26 : #include <app/server-cluster/ServerClusterInterfaceRegistry.h>
27 : #include <data-model-providers/codedriven/endpoint/EndpointInterface.h>
28 : #include <data-model-providers/codedriven/endpoint/EndpointInterfaceRegistry.h>
29 : #include <lib/support/ReadOnlyBuffer.h>
30 :
31 : namespace chip {
32 : namespace app {
33 : /**
34 : * @brief An implementation of DataModel::Provider that constructs the data model
35 : * programmatically by aggregating EndpointInterface instances.
36 : *
37 : * This provider allows applications to define their Matter device data model (endpoints,
38 : * clusters, attributes, commands) dynamically at runtime. It manages a list of EndpointInterface
39 : * objects, each representing an endpoint on the device.
40 : *
41 : * The expected usage pattern by the application is as follows:
42 : * 1. Instantiate ServerClusterInterface(s) and ServerClusterRegistration(s).
43 : * 2. Instantiate EndpointInterface(s) and EndpointInterfaceRegistration(s).
44 : * 2. Instantiate the CodeDrivenDataModelProvider.
45 : * 3. Register ServerClusterInterfaceRegistration(s) to the CodeDrivenDataModelProvider using AddCluster().
46 : * 4. Register EndpointInterfaceRegistration(s) to the CodeDrivenDataModelProvider using AddEndpoint().
47 : * Note: Step 4 MUST come after Step 3 (Endpoint needs to know about its clusters).
48 : * 5. Call Startup() on the CodeDrivenDataModelProvider.
49 : *
50 : * Note: if the CodeDrivenDataModelProvider has already been started (runtime change to add/remove Endpoints/Clusters),
51 : * the Startup() method on each ServerClusterInterface will be called when the EndpointInterface is added (Step 4).
52 : * If the provider hasn't been started, the Startup() method will be called when the provider is started (Step 5).
53 : *
54 : * TODO: Notify composition changes when the provider is started up and endpoints are added/removed at runtime.
55 : * For now, applications are responsible for handling composition changes and calling markDirty() when needed.
56 : *
57 : * Lifecycle:
58 : * - The CodeDrivenDataModelProvider stores raw pointers to EndpointInterface and ServerClusterInterface.
59 : * It does NOT take ownership. Callers must ensure these instances outlive the provider.
60 : */
61 : class CodeDrivenDataModelProvider : public DataModel::Provider
62 : {
63 : public:
64 49 : CodeDrivenDataModelProvider(PersistentStorageDelegate & storage, AttributePersistenceProvider & attributeStorage) :
65 49 : mPersistentStorageDelegate(storage), mAttributePersistenceProvider(attributeStorage)
66 49 : {}
67 :
68 : /* DataModel::Provider implementation */
69 : CHIP_ERROR Startup(DataModel::InteractionModelContext context) override;
70 : CHIP_ERROR Shutdown() override;
71 :
72 : DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request,
73 : AttributeValueEncoder & encoder) override;
74 : DataModel::ActionReturnStatus WriteAttribute(const DataModel::WriteAttributeRequest & request,
75 : AttributeValueDecoder & decoder) override;
76 :
77 : void ListAttributeWriteNotification(const ConcreteAttributePath & path, DataModel::ListWriteOperation opType,
78 : FabricIndex accessingFabric) override;
79 : std::optional<DataModel::ActionReturnStatus> InvokeCommand(const DataModel::InvokeRequest & request,
80 : TLV::TLVReader & input_arguments, CommandHandler * handler) override;
81 :
82 : /* ProviderMetadataTree implementation */
83 : CHIP_ERROR Endpoints(ReadOnlyBufferBuilder<DataModel::EndpointEntry> & out) override;
84 : CHIP_ERROR DeviceTypes(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::DeviceTypeEntry> & out) override;
85 : CHIP_ERROR ClientClusters(EndpointId endpointId, ReadOnlyBufferBuilder<ClusterId> & out) override;
86 : CHIP_ERROR ServerClusters(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::ServerClusterEntry> & out) override;
87 : CHIP_ERROR GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & out) override;
88 : CHIP_ERROR AcceptedCommands(const ConcreteClusterPath & path,
89 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & out) override;
90 : CHIP_ERROR Attributes(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder) override;
91 : CHIP_ERROR EventInfo(const ConcreteEventPath & path, DataModel::EventEntry & eventInfo) override;
92 :
93 : #if CHIP_CONFIG_USE_ENDPOINT_UNIQUE_ID
94 : CHIP_ERROR EndpointUniqueID(EndpointId endpointId, MutableCharSpan & EndpointUniqueId) override;
95 : #endif
96 :
97 : /**
98 : * @brief Adds an endpoint to the data model provider.
99 : *
100 : * This method registers an endpoint, making it part of the device's data model.
101 : * If the provider has already been started, this may trigger a Startup() call on
102 : * each ServerClusterInterface associated with the endpoint.
103 : * The Startup() call on the associated clusters will ONLY happen if this is the first
104 : * endpoint associated with the cluster (i.e. ServerClusterInterface.GetPaths() returns
105 : * at least one path with endpoint ID == registration.endpointEntry.id, and none
106 : * of the other endpoints in GetPaths() are registered yet). This ensures
107 : * the cluster is only started once, even if it is associated with multiple endpoints.
108 : *
109 : * Prerequisites:
110 : * - It MUST be called after all clusters for the endpoint have been registered with
111 : * AddCluster().
112 : * - The provided `registration` (EndpointInterfaceRegistration) must not already be
113 : * part of another list (i.e., `registration.next` must be nullptr).
114 : * - The EndpointInterface within the `registration` must be valid (i.e.,
115 : * `registration.endpointInterface` must not be nullptr).
116 : * - The `registration.endpointEntry.id` must be valid (not `kInvalidEndpointId` and
117 : * not used by another endpoint).
118 : * - The LIFETIME of `registration` must outlive the provider (or the registration must
119 : * be removed using `RemoveEndpoint` before it goes away).
120 : *
121 : * @param registration The registration object for the endpoint, containing a valid
122 : * EndpointInterface and Endpoint ID.
123 : * @return CHIP_NO_ERROR on success.
124 : * CHIP_ERROR_INVALID_ARGUMENT if `registration.next` is not nullptr or
125 : * `registration.endpointInterface` is nullptr or
126 : * `registration.endpointEntry.id` is kInvalidEndpointId.
127 : * CHIP_ERROR_DUPLICATE_KEY_ID if `registration.endpointEntry.id` is already in use.
128 : */
129 : CHIP_ERROR AddEndpoint(EndpointInterfaceRegistration & registration);
130 :
131 : /**
132 : * @brief Removes an endpoint from the data model provider.
133 : *
134 : * This method unregisters an endpoint, removing it from the device's data model.
135 : * If the provider has already been started, this might trigger a Shutdown() call on
136 : * each ServerClusterInterface associated with the endpoint.
137 : * The Shutdown() call on the associated clusters will ONLY happen if this is the last
138 : * endpoint associated with the cluster (i.e. ServerClusterInterface.GetPaths() returns
139 : * no paths with valid Endpoint IDs).
140 : *
141 : * Note: Removing an Endpoint does not remove any clusters associated with the endpoint.
142 : * Those can be removed using RemoveCluster() AFTER the endpoint has been removed.
143 :
144 : * Prerequisites:
145 : * - It MUST be called BEFORE removing associted clusters with RemoveCluster() to guarantee
146 : * the endpoint removal is atomic.
147 : * - The endpoint ID must be valid.
148 : *
149 : * @param endpointId The ID of the endpoint to remove.
150 : * @return CHIP_NO_ERROR on success.
151 : * CHIP_ERROR_NOT_FOUND if no endpoint with the given ID is registered.
152 : * CHIP_ERROR_INVALID_ARGUMENT if endpointId is kInvalidEndpointId.
153 : */
154 : CHIP_ERROR RemoveEndpoint(EndpointId endpointId, ClusterShutdownType shutdownType = ClusterShutdownType::kClusterShutdown);
155 :
156 : /**
157 : * @brief Add a ServerClusterInterface to the Data Model Provider.
158 : *
159 : * Requirements:
160 : * - entry MUST NOT be part of any other registration
161 : * - paths MUST NOT be part of any other ServerClusterInterface (i.e. only a single
162 : * registration for a given `endpointId/clusterId` path).
163 : * - The LIFETIME of entry must outlive the provider (or the entry must be unregistered
164 : * via RemoveCluster before it goes away).
165 : * - If the provider has already been started, this method must be called prior to
166 : * calling AddEndpoint() (i.e. the `endpointId` of all paths in `entry.GetPaths()`
167 : * must NOT be registered in the provider yet), otherwise this would cause non-atomic
168 : * changes to an endpoint, which is not allowed.
169 : *
170 : * @param entry The ServerClusterRegistration containing the cluster to register.
171 : * @return CHIP_NO_ERROR on success.
172 : * CHIP_ERROR_INVALID_ARGUMENT if `entry.next` is not `nullptr` or
173 : * `entry.serverClusterInterface` is `nullptr` or
174 : * `entry.serverClusterInterface.GetPaths()` is empty/invalid.
175 : * CHIP_ERROR_DUPLICATE_KEY_ID if the cluster is already registered.
176 : * CHIP_ERROR_INCORRECT_STATE if the provider has been started and an endpoint for
177 : * one of the cluster paths has already been registered.
178 : */
179 : CHIP_ERROR AddCluster(ServerClusterRegistration & entry);
180 :
181 : /**
182 : * @brief Remove a ServerClusterInterface from the Data Model Provider.
183 : *
184 : * To avoid violating the requirement of non-atomic changes to endpoints, this SHALL only be
185 : * called after all endpoints associated with the cluster have been removed using RemoveEndpoint().
186 : *
187 : * Requirements:
188 : * - entry MUST be valid
189 : * - The `endpointId` of all paths in `entry.GetPaths()` are no longer registered in the provider.
190 : *
191 : * @param entry The ServerClusterInterface to remove.
192 : * @return CHIP_NO_ERROR on success.
193 : * CHIP_ERROR_INVALID_ARGUMENT if `entry` is nullptr.
194 : * CHIP_ERROR_NOT_FOUND if the entry is not registered.
195 : * CHIP_ERROR_INCORRECT_STATE if an endpoint for one of the cluster paths is still registered.
196 : */
197 : CHIP_ERROR RemoveCluster(ServerClusterInterface * entry,
198 : ClusterShutdownType shutdownType = ClusterShutdownType::kClusterShutdown);
199 :
200 : private:
201 : EndpointInterfaceRegistry mEndpointInterfaceRegistry;
202 : ServerClusterInterfaceRegistry mServerClusterRegistry;
203 : std::optional<ServerClusterContext> mServerClusterContext;
204 : std::optional<DataModel::InteractionModelContext> mInteractionModelContext;
205 : PersistentStorageDelegate & mPersistentStorageDelegate;
206 : AttributePersistenceProvider & mAttributePersistenceProvider;
207 :
208 : /// Return the interface registered for the given endpoint ID or nullptr if one does not exist
209 : EndpointInterface * GetEndpointInterface(EndpointId endpointId);
210 :
211 : /// Return the interface registered for the given cluster path or nullptr if one does not exist
212 : ServerClusterInterface * GetServerClusterInterface(const ConcreteClusterPath & path);
213 : };
214 :
215 : } // namespace app
216 : } // namespace chip
|