Matter SDK Coverage Report
Current view: top level - data-model-providers/codegen - CodegenDataModelProvider.cpp (source / functions) Coverage Total Hit
Test: SHA:4cbce7f768f16e614f5a8ccb8cd93c92cbeae70d Lines: 98.3 % 238 234
Test Date: 2025-04-26 07:09:35 Functions: 100.0 % 21 21

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2024 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              : #include <data-model-providers/codegen/CodegenDataModelProvider.h>
      18              : 
      19              : #include <access/AccessControl.h>
      20              : #include <access/Privilege.h>
      21              : #include <app-common/zap-generated/attribute-type.h>
      22              : #include <app/CommandHandlerInterface.h>
      23              : #include <app/CommandHandlerInterfaceRegistry.h>
      24              : #include <app/ConcreteAttributePath.h>
      25              : #include <app/ConcreteClusterPath.h>
      26              : #include <app/ConcreteCommandPath.h>
      27              : #include <app/EventPathParams.h>
      28              : #include <app/GlobalAttributes.h>
      29              : #include <app/RequiredPrivilege.h>
      30              : #include <app/data-model-provider/MetadataTypes.h>
      31              : #include <app/data-model-provider/Provider.h>
      32              : #include <app/server-cluster/ServerClusterContext.h>
      33              : #include <app/server-cluster/ServerClusterInterface.h>
      34              : #include <app/util/DataModelHandler.h>
      35              : #include <app/util/IMClusterCommandHandler.h>
      36              : #include <app/util/af-types.h>
      37              : #include <app/util/attribute-metadata.h>
      38              : #include <app/util/attribute-storage.h>
      39              : #include <app/util/endpoint-config-api.h>
      40              : #include <app/util/persistence/AttributePersistenceProvider.h>
      41              : #include <app/util/persistence/DefaultAttributePersistenceProvider.h>
      42              : #include <data-model-providers/codegen/EmberMetadata.h>
      43              : #include <lib/core/CHIPError.h>
      44              : #include <lib/core/DataModelTypes.h>
      45              : #include <lib/support/CodeUtils.h>
      46              : #include <lib/support/ReadOnlyBuffer.h>
      47              : #include <lib/support/ScopedBuffer.h>
      48              : #include <lib/support/SpanSearchValue.h>
      49              : 
      50              : #include <cstdint>
      51              : #include <optional>
      52              : 
      53              : namespace chip {
      54              : namespace app {
      55              : namespace {
      56              : 
      57           79 : DataModel::AcceptedCommandEntry AcceptedCommandEntryFor(const ConcreteCommandPath & path)
      58              : {
      59           79 :     const CommandId commandId = path.mCommandId;
      60              : 
      61           79 :     DataModel::AcceptedCommandEntry entry;
      62              : 
      63           79 :     entry.commandId       = path.mCommandId;
      64           79 :     entry.invokePrivilege = RequiredPrivilege::ForInvokeCommand(path);
      65           79 :     entry.flags.Set(DataModel::CommandQualityFlags::kTimed, CommandNeedsTimedInvoke(path.mClusterId, commandId));
      66           79 :     entry.flags.Set(DataModel::CommandQualityFlags::kFabricScoped, CommandIsFabricScoped(path.mClusterId, commandId));
      67           79 :     entry.flags.Set(DataModel::CommandQualityFlags::kLargeMessage, CommandHasLargePayload(path.mClusterId, commandId));
      68              : 
      69           79 :     return entry;
      70              : }
      71              : 
      72        11462 : DataModel::ServerClusterEntry ServerClusterEntryFrom(EndpointId endpointId, const EmberAfCluster & cluster)
      73              : {
      74        11462 :     DataModel::ServerClusterEntry entry;
      75              : 
      76        11462 :     entry.clusterId = cluster.clusterId;
      77              : 
      78        11462 :     DataVersion * versionPtr = emberAfDataVersionStorage(ConcreteClusterPath(endpointId, cluster.clusterId));
      79        11462 :     if (versionPtr == nullptr)
      80              :     {
      81              : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
      82            0 :         ChipLogError(AppServer, "Failed to get data version for %d/" ChipLogFormatMEI, endpointId,
      83              :                      ChipLogValueMEI(cluster.clusterId));
      84              : #endif
      85            0 :         entry.dataVersion = 0;
      86              :     }
      87              :     else
      88              :     {
      89        11462 :         entry.dataVersion = *versionPtr;
      90              :     }
      91              : 
      92              :     // TODO: set entry flags:
      93              :     //   entry.flags.Set(ClusterQualityFlags::kDiagnosticsData)
      94              : 
      95        11462 :     return entry;
      96              : }
      97              : 
      98        63040 : DataModel::AttributeEntry AttributeEntryFrom(const ConcreteClusterPath & clusterPath, const EmberAfAttributeMetadata & attribute)
      99              : {
     100        63040 :     DataModel::AttributeEntry entry;
     101              : 
     102        63040 :     const ConcreteAttributePath attributePath(clusterPath.mEndpointId, clusterPath.mClusterId, attribute.attributeId);
     103              : 
     104        63040 :     entry.attributeId   = attribute.attributeId;
     105        63040 :     entry.readPrivilege = RequiredPrivilege::ForReadAttribute(attributePath);
     106        63040 :     if (!attribute.IsReadOnly())
     107              :     {
     108        35731 :         entry.writePrivilege = RequiredPrivilege::ForWriteAttribute(attributePath);
     109              :     }
     110              : 
     111        63040 :     entry.flags.Set(DataModel::AttributeQualityFlags::kListAttribute, (attribute.attributeType == ZCL_ARRAY_ATTRIBUTE_TYPE));
     112        63040 :     entry.flags.Set(DataModel::AttributeQualityFlags::kTimed, attribute.MustUseTimedWrite());
     113              : 
     114              :     // NOTE: we do NOT provide additional info for:
     115              :     //    - IsExternal/IsSingleton/IsAutomaticallyPersisted is not used by IM handling
     116              :     //    - IsSingleton spec defines it for CLUSTERS where as we have it for ATTRIBUTES
     117              :     //    - Several specification flags are not available (reportable, quieter reporting,
     118              :     //      fixed, source attribution)
     119              : 
     120              :     // TODO: Set additional flags:
     121              :     // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricScoped)
     122              :     // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricSensitive)
     123              :     // entry.flags.Set(DataModel::AttributeQualityFlags::kChangesOmitted)
     124        63040 :     return entry;
     125              : }
     126              : 
     127              : const ConcreteCommandPath kInvalidCommandPath(kInvalidEndpointId, kInvalidClusterId, kInvalidCommandId);
     128              : 
     129              : DefaultAttributePersistenceProvider gDefaultAttributePersistence;
     130              : 
     131              : } // namespace
     132              : 
     133          157 : CHIP_ERROR CodegenDataModelProvider::Shutdown()
     134              : {
     135          157 :     Reset();
     136          157 :     mRegistry.ClearContext();
     137          157 :     return CHIP_NO_ERROR;
     138              : }
     139              : 
     140          395 : CHIP_ERROR CodegenDataModelProvider::Startup(DataModel::InteractionModelContext context)
     141              : {
     142          395 :     ReturnErrorOnFailure(DataModel::Provider::Startup(context));
     143              : 
     144              :     // Ember NVM requires have a data model provider. attempt to create one if one is not available
     145              :     //
     146              :     // It is not a critical failure to not have one, however if one is not set up, ember NVM operations
     147              :     // will error out with a `persistence not available`.
     148          395 :     if (GetAttributePersistenceProvider() == nullptr)
     149              :     {
     150              : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
     151          393 :         ChipLogProgress(DataManagement, "Ember attribute persistence requires setting up");
     152              : #endif
     153          393 :         if (mPersistentStorageDelegate != nullptr)
     154              :         {
     155            2 :             ReturnErrorOnFailure(gDefaultAttributePersistence.Init(mPersistentStorageDelegate));
     156            2 :             SetAttributePersistenceProvider(&gDefaultAttributePersistence);
     157              : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
     158              :         }
     159              :         else
     160              :         {
     161          391 :             ChipLogError(DataManagement, "No storage delegate available, will not set up attribute persistence.");
     162              : #endif
     163              :         }
     164              :     }
     165              : 
     166          395 :     InitDataModelForTesting();
     167              : 
     168          395 :     return mRegistry.SetContext(ServerClusterContext{
     169              :         .provider           = this,
     170          395 :         .storage            = mPersistentStorageDelegate,
     171          395 :         .interactionContext = &mContext,
     172          395 :     });
     173              : }
     174              : 
     175            6 : std::optional<DataModel::ActionReturnStatus> CodegenDataModelProvider::InvokeCommand(const DataModel::InvokeRequest & request,
     176              :                                                                                      TLV::TLVReader & input_arguments,
     177              :                                                                                      CommandHandler * handler)
     178              : {
     179            6 :     if (auto * cluster = mRegistry.Get(request.path); cluster != nullptr)
     180              :     {
     181            1 :         return cluster->InvokeCommand(request, input_arguments, handler);
     182              :     }
     183              : 
     184              :     CommandHandlerInterface * handler_interface =
     185            5 :         CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(request.path.mEndpointId, request.path.mClusterId);
     186              : 
     187            5 :     if (handler_interface)
     188              :     {
     189            3 :         CommandHandlerInterface::HandlerContext context(*handler, request.path, input_arguments);
     190            3 :         handler_interface->InvokeCommand(context);
     191              : 
     192              :         // If the command was handled, don't proceed any further and return successfully.
     193            3 :         if (context.mCommandHandled)
     194              :         {
     195            3 :             return std::nullopt;
     196              :         }
     197              :     }
     198              : 
     199              :     // Ember always sets the return in the handler
     200            2 :     DispatchSingleClusterCommand(request.path, input_arguments, handler);
     201            2 :     return std::nullopt;
     202              : }
     203              : 
     204         2938 : CHIP_ERROR CodegenDataModelProvider::Endpoints(ReadOnlyBufferBuilder<DataModel::EndpointEntry> & builder)
     205              : {
     206         2938 :     const uint16_t endpointCount = emberAfEndpointCount();
     207              : 
     208         2938 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(endpointCount));
     209              : 
     210        17197 :     for (uint16_t endpointIndex = 0; endpointIndex < endpointCount; endpointIndex++)
     211              :     {
     212        14259 :         if (!emberAfEndpointIndexIsEnabled(endpointIndex))
     213              :         {
     214         4333 :             continue;
     215              :         }
     216              : 
     217              :         DataModel::EndpointEntry entry;
     218         9926 :         entry.id       = emberAfEndpointFromIndex(endpointIndex);
     219         9926 :         entry.parentId = emberAfParentEndpointFromIndex(endpointIndex);
     220              : 
     221         9926 :         switch (GetCompositionForEndpointIndex(endpointIndex))
     222              :         {
     223         8446 :         case EndpointComposition::kFullFamily:
     224         8446 :             entry.compositionPattern = DataModel::EndpointCompositionPattern::kFullFamily;
     225         8446 :             break;
     226         1480 :         case EndpointComposition::kTree:
     227              :         case EndpointComposition::kInvalid: // should NOT happen, but force compiler to check we validate all versions
     228         1480 :             entry.compositionPattern = DataModel::EndpointCompositionPattern::kTree;
     229         1480 :             break;
     230              :         }
     231         9926 :         ReturnErrorOnFailure(builder.Append(entry));
     232              :     }
     233              : 
     234         2938 :     return CHIP_NO_ERROR;
     235              : }
     236              : 
     237            3 : std::optional<unsigned> CodegenDataModelProvider::TryFindEndpointIndex(EndpointId id) const
     238              : {
     239            3 :     const uint16_t lastEndpointIndex = emberAfEndpointCount();
     240              : 
     241            6 :     if ((mEndpointIterationHint < lastEndpointIndex) && emberAfEndpointIndexIsEnabled(mEndpointIterationHint) &&
     242            3 :         (id == emberAfEndpointFromIndex(mEndpointIterationHint)))
     243              :     {
     244            1 :         return std::make_optional(mEndpointIterationHint);
     245              :     }
     246              : 
     247              :     // Linear search, this may be slow
     248            2 :     uint16_t idx = emberAfIndexFromEndpoint(id);
     249            2 :     if (idx == kEmberInvalidEndpointIndex)
     250              :     {
     251            0 :         return std::nullopt;
     252              :     }
     253              : 
     254            2 :     return std::make_optional<unsigned>(idx);
     255              : }
     256              : 
     257         8465 : CHIP_ERROR CodegenDataModelProvider::ServerClusters(EndpointId endpointId,
     258              :                                                     ReadOnlyBufferBuilder<DataModel::ServerClusterEntry> & builder)
     259              : {
     260         8465 :     const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
     261              : 
     262         8465 :     VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
     263         8454 :     VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
     264         8454 :     VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
     265              : 
     266              :     // We build the cluster list by merging two lists:
     267              :     //   - mRegistry items from ServerClusterInterfaces
     268              :     //   - ember metadata clusters
     269              :     //
     270              :     // This is done because `ServerClusterInterface` allows full control for all its metadata,
     271              :     // in particular `data version` and `flags`.
     272              :     //
     273              :     // To allow cluster implementations to be incrementally converted to storing their own data versions,
     274              :     // instead of relying on the out-of-band emberAfDataVersionStorage, first check for clusters that are
     275              :     // using the new data version storage and are registered via ServerClusterInterfaceRegistry, then fill
     276              :     // in the data versions for the rest via the out-of-band mechanism.
     277              : 
     278              :     // assume the clusters on endpoint does not change in between these two loops
     279         8454 :     auto clusters               = mRegistry.ClustersOnEndpoint(endpointId);
     280         8454 :     size_t registryClusterCount = 0;
     281         8455 :     for ([[maybe_unused]] auto _ : clusters)
     282              :     {
     283            1 :         registryClusterCount++;
     284              :     }
     285              : 
     286         8454 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(registryClusterCount));
     287              : 
     288         8454 :     ReadOnlyBufferBuilder<ClusterId> knownClustersBuilder;
     289         8454 :     ReturnErrorOnFailure(knownClustersBuilder.EnsureAppendCapacity(registryClusterCount));
     290         8455 :     for (const auto clusterId : mRegistry.ClustersOnEndpoint(endpointId))
     291              :     {
     292            1 :         ConcreteClusterPath path(endpointId, clusterId);
     293            1 :         ServerClusterInterface * cluster = mRegistry.Get(path);
     294              : 
     295              :         // path MUST be valid: we just got it from iterating our registrations...
     296            1 :         VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_INTERNAL);
     297              : 
     298            1 :         ReturnErrorOnFailure(builder.Append({
     299              :             .clusterId   = path.mClusterId,
     300              :             .dataVersion = cluster->GetDataVersion(path),
     301              :             .flags       = cluster->GetClusterFlags(path),
     302              :         }));
     303            1 :         ReturnErrorOnFailure(knownClustersBuilder.Append(path.mClusterId));
     304              :     }
     305              : 
     306         8454 :     ReadOnlyBuffer<ClusterId> knownClusters = knownClustersBuilder.TakeBuffer();
     307              : 
     308         8454 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ true)));
     309              : 
     310         8454 :     const EmberAfCluster * begin = endpoint->cluster;
     311         8454 :     const EmberAfCluster * end   = endpoint->cluster + endpoint->clusterCount;
     312        21381 :     for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
     313              :     {
     314        12927 :         if (!cluster->IsServer())
     315              :         {
     316         1464 :             continue;
     317              :         }
     318              : 
     319              :         // linear search as this is a somewhat compact number list, so performance is probably not too bad
     320              :         // This results in smaller code than some memory allocation + std::sort + std::binary_search
     321        11463 :         bool found = false;
     322        11464 :         for (ClusterId clusterId : knownClusters)
     323              :         {
     324            2 :             if (clusterId == cluster->clusterId)
     325              :             {
     326            1 :                 found = true;
     327            1 :                 break;
     328              :             }
     329              :         }
     330        11463 :         if (found)
     331              :         {
     332              :             // value already filled from the ServerClusterRegistry. That one has the correct/overriden
     333              :             // flags and data version
     334            1 :             continue;
     335              :         }
     336              : 
     337        11462 :         ReturnErrorOnFailure(builder.Append(ServerClusterEntryFrom(endpointId, *cluster)));
     338              :     }
     339              : 
     340         8454 :     return CHIP_NO_ERROR;
     341         8454 : }
     342              : 
     343        15575 : CHIP_ERROR CodegenDataModelProvider::Attributes(const ConcreteClusterPath & path,
     344              :                                                 ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder)
     345              : {
     346        15575 :     if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
     347              :     {
     348            1 :         return cluster->Attributes(path, builder);
     349              :     }
     350              : 
     351        15574 :     const EmberAfCluster * cluster = FindServerCluster(path);
     352              : 
     353        15574 :     VerifyOrReturnValue(cluster != nullptr, CHIP_ERROR_NOT_FOUND);
     354        14873 :     VerifyOrReturnValue(cluster->attributeCount > 0, CHIP_NO_ERROR);
     355        14873 :     VerifyOrReturnValue(cluster->attributes != nullptr, CHIP_NO_ERROR);
     356              : 
     357              :     // TODO: if ember would encode data in AttributeEntry form, we could reference things directly (shorter code,
     358              :     //       although still allocation overhead due to global attributes not in metadata)
     359              :     //
     360              :     // We have Attributes from ember + global attributes that are NOT in ember metadata.
     361              :     // We have to report them all
     362        14873 :     constexpr size_t kGlobalAttributeNotInMetadataCount = MATTER_ARRAY_SIZE(GlobalAttributesNotInMetadata);
     363              : 
     364        14873 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(cluster->attributeCount + kGlobalAttributeNotInMetadataCount));
     365              : 
     366        14873 :     Span<const EmberAfAttributeMetadata> attributeSpan(cluster->attributes, cluster->attributeCount);
     367              : 
     368        77913 :     for (auto & attribute : attributeSpan)
     369              :     {
     370        63040 :         ReturnErrorOnFailure(builder.Append(AttributeEntryFrom(path, attribute)));
     371              :     }
     372              : 
     373              :     // This "GlobalListEntry" is specific for metadata that ember does not include
     374              :     // in its attribute list metadata.
     375              :     //
     376              :     // By spec these Attribute/AcceptedCommands/GeneratedCommants lists are:
     377              :     //   - lists of elements
     378              :     //   - read-only, with read privilege view
     379              :     //   - fixed value (no such flag exists, so this is not a quality flag we set/track)
     380        14873 :     DataModel::AttributeEntry globalListEntry;
     381              : 
     382        14873 :     globalListEntry.readPrivilege = Access::Privilege::kView;
     383        14873 :     globalListEntry.flags.Set(DataModel::AttributeQualityFlags::kListAttribute);
     384              : 
     385        59492 :     for (auto & attribute : GlobalAttributesNotInMetadata)
     386              :     {
     387        44619 :         globalListEntry.attributeId = attribute;
     388        44619 :         ReturnErrorOnFailure(builder.Append(globalListEntry));
     389              :     }
     390              : 
     391        14873 :     return CHIP_NO_ERROR;
     392              : }
     393              : 
     394            4 : CHIP_ERROR CodegenDataModelProvider::ClientClusters(EndpointId endpointId, ReadOnlyBufferBuilder<ClusterId> & builder)
     395              : {
     396            4 :     const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
     397              : 
     398            4 :     VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
     399            2 :     VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
     400            2 :     VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
     401              : 
     402            2 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ false)));
     403              : 
     404            2 :     const EmberAfCluster * begin = endpoint->cluster;
     405            2 :     const EmberAfCluster * end   = endpoint->cluster + endpoint->clusterCount;
     406           10 :     for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
     407              :     {
     408            8 :         if (!cluster->IsClient())
     409              :         {
     410            4 :             continue;
     411              :         }
     412            4 :         ReturnErrorOnFailure(builder.Append(cluster->clusterId));
     413              :     }
     414              : 
     415            2 :     return CHIP_NO_ERROR;
     416              : }
     417              : 
     418        16379 : const EmberAfCluster * CodegenDataModelProvider::FindServerCluster(const ConcreteClusterPath & path)
     419              : {
     420        31705 :     if (mPreviouslyFoundCluster.has_value() && (mPreviouslyFoundCluster->path == path) &&
     421        15326 :         (mEmberMetadataStructureGeneration == emberAfMetadataStructureGeneration()))
     422              : 
     423              :     {
     424        15217 :         return mPreviouslyFoundCluster->cluster;
     425              :     }
     426              : 
     427         1162 :     const EmberAfCluster * cluster = emberAfFindServerCluster(path.mEndpointId, path.mClusterId);
     428         1162 :     if (cluster != nullptr)
     429              :     {
     430          448 :         mPreviouslyFoundCluster           = std::make_optional<ClusterReference>(path, cluster);
     431          448 :         mEmberMetadataStructureGeneration = emberAfMetadataStructureGeneration();
     432              :     }
     433         1162 :     return cluster;
     434              : }
     435              : 
     436          429 : CHIP_ERROR CodegenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path,
     437              :                                                       ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder)
     438              : {
     439          429 :     if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
     440              :     {
     441            1 :         return cluster->AcceptedCommands(path, builder);
     442              :     }
     443              : 
     444              :     // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
     445              :     // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
     446              :     // it claims to support.
     447          428 :     const EmberAfCluster * serverCluster = FindServerCluster(path);
     448          428 :     VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
     449              : 
     450              :     CommandHandlerInterface * interface =
     451          421 :         CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
     452          421 :     if (interface != nullptr)
     453              :     {
     454           16 :         size_t commandCount = 0;
     455              : 
     456           16 :         CHIP_ERROR err = interface->EnumerateAcceptedCommands(
     457              :             path,
     458            6 :             [](CommandId id, void * context) -> Loop {
     459            6 :                 *reinterpret_cast<size_t *>(context) += 1;
     460            6 :                 return Loop::Continue;
     461              :             },
     462              :             reinterpret_cast<void *>(&commandCount));
     463              : 
     464           16 :         if (err == CHIP_NO_ERROR)
     465              :         {
     466              :             using EnumerationData = struct
     467              :             {
     468              :                 ConcreteCommandPath commandPath;
     469              :                 ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> * acceptedCommandList;
     470              :                 CHIP_ERROR processingError;
     471              :             };
     472              : 
     473            8 :             EnumerationData enumerationData;
     474            8 :             enumerationData.commandPath         = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
     475            8 :             enumerationData.processingError     = CHIP_NO_ERROR;
     476            8 :             enumerationData.acceptedCommandList = &builder;
     477              : 
     478            8 :             ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
     479              : 
     480           14 :             ReturnErrorOnFailure(interface->EnumerateAcceptedCommands(
     481              :                 path,
     482              :                 [](CommandId commandId, void * context) -> Loop {
     483              :                     auto input                    = reinterpret_cast<EnumerationData *>(context);
     484              :                     input->commandPath.mCommandId = commandId;
     485              :                     CHIP_ERROR appendError        = input->acceptedCommandList->Append(AcceptedCommandEntryFor(input->commandPath));
     486              :                     if (appendError != CHIP_NO_ERROR)
     487              :                     {
     488              :                         input->processingError = appendError;
     489              :                         return Loop::Break;
     490              :                     }
     491              :                     return Loop::Continue;
     492              :                 },
     493              :                 reinterpret_cast<void *>(&enumerationData)));
     494            8 :             ReturnErrorOnFailure(enumerationData.processingError);
     495              : 
     496              :             // the two invocations MUST return the same sizes.
     497            8 :             VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL);
     498            8 :             return CHIP_NO_ERROR;
     499              :         }
     500            8 :         VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
     501              :     }
     502              : 
     503          413 :     VerifyOrReturnError(serverCluster->acceptedCommandList != nullptr, CHIP_NO_ERROR);
     504              : 
     505           28 :     const chip::CommandId * endOfList = serverCluster->acceptedCommandList;
     506          101 :     while (*endOfList != kInvalidCommandId)
     507              :     {
     508           73 :         endOfList++;
     509              :     }
     510           28 :     const auto commandCount = static_cast<size_t>(endOfList - serverCluster->acceptedCommandList);
     511              : 
     512              :     // TODO: if ember would store command entries, we could simplify this code to use static data
     513           28 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
     514              : 
     515           28 :     ConcreteCommandPath commandPath = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
     516          101 :     for (const chip::CommandId * p = serverCluster->acceptedCommandList; p != endOfList; p++)
     517              :     {
     518           73 :         commandPath.mCommandId = *p;
     519           73 :         ReturnErrorOnFailure(builder.Append(AcceptedCommandEntryFor(commandPath)));
     520              :     }
     521              : 
     522           28 :     return CHIP_NO_ERROR;
     523              : }
     524              : 
     525          378 : CHIP_ERROR CodegenDataModelProvider::GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & builder)
     526              : {
     527          378 :     if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
     528              :     {
     529            1 :         return cluster->GeneratedCommands(path, builder);
     530              :     }
     531              : 
     532              :     // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
     533              :     // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
     534              :     // it claims to support.
     535          377 :     const EmberAfCluster * serverCluster = FindServerCluster(path);
     536          377 :     VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
     537              : 
     538              :     CommandHandlerInterface * interface =
     539          371 :         CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
     540          371 :     if (interface != nullptr)
     541              :     {
     542            4 :         size_t commandCount = 0;
     543              : 
     544            4 :         CHIP_ERROR err = interface->EnumerateGeneratedCommands(
     545              :             path,
     546            1 :             [](CommandId id, void * context) -> Loop {
     547            1 :                 *reinterpret_cast<size_t *>(context) += 1;
     548            1 :                 return Loop::Continue;
     549              :             },
     550              :             reinterpret_cast<void *>(&commandCount));
     551              : 
     552            4 :         if (err == CHIP_NO_ERROR)
     553              :         {
     554            2 :             ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
     555              : 
     556              :             using EnumerationData = struct
     557              :             {
     558              :                 ReadOnlyBufferBuilder<CommandId> * generatedCommandList;
     559              :                 CHIP_ERROR processingError;
     560              :             };
     561              :             EnumerationData enumerationData;
     562            2 :             enumerationData.processingError      = CHIP_NO_ERROR;
     563            2 :             enumerationData.generatedCommandList = &builder;
     564              : 
     565            3 :             ReturnErrorOnFailure(interface->EnumerateGeneratedCommands(
     566              :                 path,
     567              :                 [](CommandId id, void * context) -> Loop {
     568              :                     auto input = reinterpret_cast<EnumerationData *>(context);
     569              : 
     570              :                     CHIP_ERROR appendError = input->generatedCommandList->Append(id);
     571              :                     if (appendError != CHIP_NO_ERROR)
     572              :                     {
     573              :                         input->processingError = appendError;
     574              :                         return Loop::Break;
     575              :                     }
     576              :                     return Loop::Continue;
     577              :                 },
     578              :                 reinterpret_cast<void *>(&enumerationData)));
     579            2 :             ReturnErrorOnFailure(enumerationData.processingError);
     580              : 
     581              :             // the two invocations MUST return the same sizes.
     582            2 :             VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL);
     583            2 :             return CHIP_NO_ERROR;
     584              :         }
     585            2 :         VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
     586              :     }
     587              : 
     588          369 :     VerifyOrReturnError(serverCluster->generatedCommandList != nullptr, CHIP_NO_ERROR);
     589              : 
     590            2 :     const chip::CommandId * endOfList = serverCluster->generatedCommandList;
     591            6 :     while (*endOfList != kInvalidCommandId)
     592              :     {
     593            4 :         endOfList++;
     594              :     }
     595            2 :     const auto commandCount = static_cast<size_t>(endOfList - serverCluster->generatedCommandList);
     596            2 :     return builder.ReferenceExisting({ serverCluster->generatedCommandList, commandCount });
     597              : }
     598              : 
     599          388 : void CodegenDataModelProvider::InitDataModelForTesting()
     600              : {
     601              :     // Call the Ember-specific InitDataModelHandler
     602          388 :     InitDataModelHandler();
     603          388 : }
     604              : 
     605            3 : CHIP_ERROR CodegenDataModelProvider::DeviceTypes(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::DeviceTypeEntry> & builder)
     606              : {
     607            3 :     std::optional<unsigned> endpoint_index = TryFindEndpointIndex(endpointId);
     608            3 :     if (!endpoint_index.has_value())
     609              :     {
     610            0 :         return {};
     611              :     }
     612              : 
     613            3 :     CHIP_ERROR err = CHIP_NO_ERROR;
     614              : 
     615            3 :     return builder.ReferenceExisting(emberAfDeviceTypeListFromEndpointIndex(*endpoint_index, err));
     616              : }
     617              : 
     618            2 : CHIP_ERROR CodegenDataModelProvider::SemanticTags(EndpointId endpointId, ReadOnlyBufferBuilder<SemanticTag> & builder)
     619              : {
     620            2 :     DataModel::Provider::SemanticTag semanticTag;
     621            2 :     size_t count = 0;
     622              : 
     623            5 :     while (GetSemanticTagForEndpointAtIndex(endpointId, count, semanticTag) == CHIP_NO_ERROR)
     624              :     {
     625            3 :         count++;
     626              :     }
     627              : 
     628            2 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(count));
     629              : 
     630            5 :     for (size_t idx = 0; idx < count; idx++)
     631              :     {
     632            3 :         ReturnErrorOnFailure(GetSemanticTagForEndpointAtIndex(endpointId, idx, semanticTag));
     633            3 :         ReturnErrorOnFailure(builder.Append(semanticTag));
     634              :     }
     635              : 
     636            2 :     return CHIP_NO_ERROR;
     637              : }
     638              : 
     639              : } // namespace app
     640              : } // namespace chip
        

Generated by: LCOV version 2.0-1