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

Generated by: LCOV version 2.0-1