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