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 : void Temporary_ReportAttributeChanged(const AttributePathParams & path) override;
93 :
94 : #if CHIP_CONFIG_USE_ENDPOINT_UNIQUE_ID
95 : CHIP_ERROR EndpointUniqueID(EndpointId endpointId, MutableCharSpan & EndpointUniqueId) override;
96 : #endif
97 :
98 : /**
99 : * @brief Adds an endpoint to the data model provider.
100 : *
101 : * This method registers an endpoint, making it part of the device's data model.
102 : * If the provider has already been started, this may trigger a Startup() call on
103 : * each ServerClusterInterface associated with the endpoint.
104 : * The Startup() call on the associated clusters will ONLY happen if this is the first
105 : * endpoint associated with the cluster (i.e. ServerClusterInterface.GetPaths() returns
106 : * at least one path with endpoint ID == registration.endpointEntry.id, and none
107 : * of the other endpoints in GetPaths() are registered yet). This ensures
108 : * the cluster is only started once, even if it is associated with multiple endpoints.
109 : *
110 : * Prerequisites:
111 : * - It MUST be called after all clusters for the endpoint have been registered with
112 : * AddCluster().
113 : * - The provided `registration` (EndpointInterfaceRegistration) must not already be
114 : * part of another list (i.e., `registration.next` must be nullptr).
115 : * - The EndpointInterface within the `registration` must be valid (i.e.,
116 : * `registration.endpointInterface` must not be nullptr).
117 : * - The `registration.endpointEntry.id` must be valid (not `kInvalidEndpointId` and
118 : * not used by another endpoint).
119 : * - The LIFETIME of `registration` must outlive the provider (or the registration must
120 : * be removed using `RemoveEndpoint` before it goes away).
121 : *
122 : * @param registration The registration object for the endpoint, containing a valid
123 : * EndpointInterface and Endpoint ID.
124 : * @return CHIP_NO_ERROR on success.
125 : * CHIP_ERROR_INVALID_ARGUMENT if `registration.next` is not nullptr or
126 : * `registration.endpointInterface` is nullptr or
127 : * `registration.endpointEntry.id` is kInvalidEndpointId.
128 : * CHIP_ERROR_DUPLICATE_KEY_ID if `registration.endpointEntry.id` is already in use.
129 : */
130 : CHIP_ERROR AddEndpoint(EndpointInterfaceRegistration & registration);
131 :
132 : /**
133 : * @brief Removes an endpoint from the data model provider.
134 : *
135 : * This method unregisters an endpoint, removing it from the device's data model.
136 : * If the provider has already been started, this might trigger a Shutdown() call on
137 : * each ServerClusterInterface associated with the endpoint.
138 : * The Shutdown() call on the associated clusters will ONLY happen if this is the last
139 : * endpoint associated with the cluster (i.e. ServerClusterInterface.GetPaths() returns
140 : * no paths with valid Endpoint IDs).
141 : *
142 : * Note: Removing an Endpoint does not remove any clusters associated with the endpoint.
143 : * Those can be removed using RemoveCluster() AFTER the endpoint has been removed.
144 :
145 : * Prerequisites:
146 : * - It MUST be called BEFORE removing associted clusters with RemoveCluster() to guarantee
147 : * the endpoint removal is atomic.
148 : * - The endpoint ID must be valid.
149 : *
150 : * @param endpointId The ID of the endpoint to remove.
151 : * @return CHIP_NO_ERROR on success.
152 : * CHIP_ERROR_NOT_FOUND if no endpoint with the given ID is registered.
153 : * CHIP_ERROR_INVALID_ARGUMENT if endpointId is kInvalidEndpointId.
154 : */
155 : CHIP_ERROR RemoveEndpoint(EndpointId endpointId, ClusterShutdownType shutdownType = ClusterShutdownType::kClusterShutdown);
156 :
157 : /**
158 : * @brief Add a ServerClusterInterface to the Data Model Provider.
159 : *
160 : * Requirements:
161 : * - entry MUST NOT be part of any other registration
162 : * - paths MUST NOT be part of any other ServerClusterInterface (i.e. only a single
163 : * registration for a given `endpointId/clusterId` path).
164 : * - The LIFETIME of entry must outlive the provider (or the entry must be unregistered
165 : * via RemoveCluster before it goes away).
166 : * - If the provider has already been started, this method must be called prior to
167 : * calling AddEndpoint() (i.e. the `endpointId` of all paths in `entry.GetPaths()`
168 : * must NOT be registered in the provider yet), otherwise this would cause non-atomic
169 : * changes to an endpoint, which is not allowed.
170 : *
171 : * @param entry The ServerClusterRegistration containing the cluster to register.
172 : * @return CHIP_NO_ERROR on success.
173 : * CHIP_ERROR_INVALID_ARGUMENT if `entry.next` is not `nullptr` or
174 : * `entry.serverClusterInterface` is `nullptr` or
175 : * `entry.serverClusterInterface.GetPaths()` is empty/invalid.
176 : * CHIP_ERROR_DUPLICATE_KEY_ID if the cluster is already registered.
177 : * CHIP_ERROR_INCORRECT_STATE if the provider has been started and an endpoint for
178 : * one of the cluster paths has already been registered.
179 : */
180 : CHIP_ERROR AddCluster(ServerClusterRegistration & entry);
181 :
182 : /**
183 : * @brief Remove a ServerClusterInterface from the Data Model Provider.
184 : *
185 : * To avoid violating the requirement of non-atomic changes to endpoints, this SHALL only be
186 : * called after all endpoints associated with the cluster have been removed using RemoveEndpoint().
187 : *
188 : * Requirements:
189 : * - entry MUST be valid
190 : * - The `endpointId` of all paths in `entry.GetPaths()` are no longer registered in the provider.
191 : *
192 : * @param entry The ServerClusterInterface to remove.
193 : * @return CHIP_NO_ERROR on success.
194 : * CHIP_ERROR_INVALID_ARGUMENT if `entry` is nullptr.
195 : * CHIP_ERROR_NOT_FOUND if the entry is not registered.
196 : * CHIP_ERROR_INCORRECT_STATE if an endpoint for one of the cluster paths is still registered.
197 : */
198 : CHIP_ERROR RemoveCluster(ServerClusterInterface * entry,
199 : ClusterShutdownType shutdownType = ClusterShutdownType::kClusterShutdown);
200 :
201 : private:
202 : EndpointInterfaceRegistry mEndpointInterfaceRegistry;
203 : ServerClusterInterfaceRegistry mServerClusterRegistry;
204 : std::optional<ServerClusterContext> mServerClusterContext;
205 : std::optional<DataModel::InteractionModelContext> mInteractionModelContext;
206 : PersistentStorageDelegate & mPersistentStorageDelegate;
207 : AttributePersistenceProvider & mAttributePersistenceProvider;
208 :
209 : /// Return the interface registered for the given endpoint ID or nullptr if one does not exist
210 : EndpointInterface * GetEndpointInterface(EndpointId endpointId);
211 :
212 : /// Return the interface registered for the given cluster path or nullptr if one does not exist
213 : ServerClusterInterface * GetServerClusterInterface(const ConcreteClusterPath & path);
214 : };
215 :
216 : } // namespace app
217 : } // namespace chip
|