Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changing AcceptedCommands Interface #37646

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a6c102c
Changing AcceptedCommands Interface
ratgr Feb 19, 2025
60c855f
Update CHI::EnumerateGeneratedCommands to use builder
ratgr Feb 23, 2025
97a16db
Added Builder initializer support
ratgr Feb 23, 2025
0aeb4e5
Update all cluster Implentations of the CHI interface
ratgr Feb 23, 2025
c9918d2
Restyled by whitespace
restyled-commits Feb 23, 2025
9685fee
Fixing Checks
ratgr Feb 23, 2025
1e118db
Size_t
ratgr Feb 23, 2025
3a551c1
[PATCH] Restyled by clang-format
ratgr Feb 24, 2025
ff54464
Updated attributes
ratgr Feb 24, 2025
6511c42
Restyled by whitespace
restyled-commits Feb 24, 2025
77a4f9f
Restyled by clang-format
restyled-commits Feb 24, 2025
7cb4b5e
Clear up Access Permissions
ratgr Feb 24, 2025
dde4481
remove unnecesary file
ratgr Feb 24, 2025
e55e237
Restyled by whitespace
restyled-commits Feb 24, 2025
5a0a934
Restyled by clang-format
restyled-commits Feb 24, 2025
62429e8
Fix to networkCommisioning
ratgr Feb 24, 2025
6f5ea52
AcceptedCommands should return early
ratgr Feb 25, 2025
319d9aa
Check Error for CHIP_ERROR_NOT_IMPLEMENTED
ratgr Feb 25, 2025
8e92e77
Make sure there is space before appending
ratgr Feb 25, 2025
777a609
Fix Darwing Clangtidy issue
ratgr Feb 26, 2025
5f53224
Merge remote-tracking branch 'origin/master' into command-handler-int…
ratgr Feb 26, 2025
9671d96
Fixing dependency movement
ratgr Feb 26, 2025
33a0663
Restyled by clang-format
restyled-commits Feb 26, 2025
6e4a9e9
Remove Forward declaration
ratgr Feb 26, 2025
e4a4968
Use builder instead of static array for network comissioning
ratgr Feb 26, 2025
10cd5d3
Update Comment on new AppendElements for initializer list
ratgr Feb 26, 2025
731222e
Better phrasing in comments
ratgr Feb 26, 2025
98ea178
TestClusterCommandHandler::EnumerateAcceptedCommands don't use dynami…
ratgr Feb 26, 2025
3f4a906
Update src/app/CommandHandlerInterface.h
ratgr Feb 26, 2025
3037bfc
Documenting Upgrading changes
ratgr Feb 26, 2025
f4cdd15
Quick Fix
ratgr Feb 26, 2025
7f56e9f
Update docs/upgrading.md
ratgr Feb 26, 2025
a20600e
Making all Privileges Explicit
ratgr Feb 26, 2025
afc80b1
Remove return of stack ptr
ratgr Feb 26, 2025
8c10c16
Allocate only once in enumeration
ratgr Feb 27, 2025
2bf9949
Privileges in ResetWatermarks up to spec
ratgr Feb 28, 2025
8b8947b
remove chip from Access::Privilege;
ratgr Feb 28, 2025
316072b
Rephrasing Upgrading comment
ratgr Feb 28, 2025
9c54176
Some more fixes
ratgr Feb 28, 2025
088f797
Update src/app/clusters/network-commissioning/network-commissioning.cpp
ratgr Feb 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions docs/upgrading.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,87 @@ To use default attribute persistence, you need to pass in a
`PersistentStorageDelegate` to `CodegenDataModelProviderInstance`. See example
changes in [36658](https://github.com/project-chip/connectedhomeip/pull/36658)
).

### Decoupling of Command Handler Interface from Ember

#### EnumerateGeneratedCommands

Changed the old callback based iteration, into a ListBuilder based approach for
the Enumeration of Generated Commands

`CommandHandlerInterface::EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)`
becomes
`CommandHandlerInterface::EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, ListBuilder<CommandId> & builder)`

Changes for implementation

- old

```cpp
for (auto && cmd : { ScanNetworksResponse::Id, NetworkConfigResponse::Id, ConnectNetworkResponse::Id })
{
VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/);
}
```

- new

```cpp
ReturnErrorOnFailure(
builder.AppendElements({ ScanNetworksResponse::Id, NetworkConfigResponse::Id, ConnectNetworkResponse::Id })
)
```

#### EnumerateAcceptedCommands

Expanded `EnumerateAcceptedCommands` Interface to provide the Access Information
(Attributes and Qualities) using a ListBuilder

`CommandHandlerInterface::EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)`
becomes
`CommandHandlerInterface::EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, ListBuilder<AcceptedCommandEntry> & builder)`

Changes for implementation:

- Old

```cpp
for (auto && cmd : {
Disable::Id,
EnableCharging::Id,
})
{
VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/);
}

if (HasFeature(Feature::kV2x))
{
VerifyOrExit(callback(EnableDischarging::Id, context) == Loop::Continue, /**/);
}
```

- new

```cpp
using QF = DataModel::CommandQualityFlags;
using Priv = Access::Privilege;

ReturnErrorOnFailure(builder.AppendElements({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this not need EnsureAppendCapacity before AppendElements?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, AppendElements already ensures capacity before, only when using Append is necessary

{ Disable::Id, QF::kTimed, Priv::kOperate },
{ EnableCharging::Id, QF::kTimed, Priv::kOperate },
}));

if (HasFeature(Feature::kV2x))
{
ReturnErrorOnFailure(builder.EnsureAppendCapacity(1));
ReturnErrorOnFailure(
builder.Append({ EnableDischarging::Id, QF::kTimed, Priv::kOperate})
);
}

```

Important Notes:

Use `EnsureAppendCapacity` before `ListBuilder::Append` to prevent buffer
overflow when appending a single element, this function never allocates.
8 changes: 5 additions & 3 deletions src/app/CommandHandlerInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
#include <app/CommandHandler.h>
#include <app/ConcreteClusterPath.h>
#include <app/ConcreteCommandPath.h>
#include <app/data-model-provider/MetadataList.h>
#include <app/data-model-provider/MetadataTypes.h>
#include <app/data-model/Decode.h>
#include <app/data-model/List.h> // So we can encode lists
#include <lib/core/DataModelTypes.h>
#include <lib/support/Iterators.h>

namespace chip {
namespace app {

/*
* This interface permits applications to register a server-side command handler
* at run-time for a given cluster. The handler can either be configured to handle all endpoints
Expand Down Expand Up @@ -122,7 +123,8 @@ class CommandHandlerInterface
* This is used by callbacks that just look for a particular value in the
* list.
*/
virtual CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
virtual CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster,
DataModel::ListBuilder<DataModel::AcceptedCommandEntry> & builder)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
Expand All @@ -146,7 +148,7 @@ class CommandHandlerInterface
* This is used by callbacks that just look for a particular value in the
* list.
*/
virtual CHIP_ERROR EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
virtual CHIP_ERROR EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, DataModel::ListBuilder<CommandId> & builder)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,49 +104,51 @@ CHIP_ERROR Instance::Read(const ConcreteReadAttributePath & aPath, AttributeValu
}

// CommandHandlerInterface
CHIP_ERROR Instance::EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
CHIP_ERROR Instance::EnumerateAcceptedCommands(const ConcreteClusterPath & cluster,
DataModel::ListBuilder<DataModel::AcceptedCommandEntry> & builder)
{
using namespace Commands;
using Priv = Access::Privilege;

ReturnErrorOnFailure(builder.EnsureAppendCapacity(8)); // Ensure we have capacity for all possible commands

if (HasFeature(Feature::kPowerAdjustment))
{
for (auto && cmd : {
PowerAdjustRequest::Id,
CancelPowerAdjustRequest::Id,
})
{
VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/);
}
ReturnErrorOnFailure(builder.AppendElements({
{ PowerAdjustRequest::Id, {}, Priv::kOperate }, //
{ CancelPowerAdjustRequest::Id, {}, Priv::kOperate } //
}));
}

if (HasFeature(Feature::kStartTimeAdjustment))
{
VerifyOrExit(callback(StartTimeAdjustRequest::Id, context) == Loop::Continue, /**/);
ReturnErrorOnFailure(builder.Append({ StartTimeAdjustRequest::Id, {}, Priv::kOperate }));
}

if (HasFeature(Feature::kPausable))
{
VerifyOrExit(callback(PauseRequest::Id, context) == Loop::Continue, /**/);
VerifyOrExit(callback(ResumeRequest::Id, context) == Loop::Continue, /**/);
ReturnErrorOnFailure(builder.AppendElements({
{ PauseRequest::Id, {}, Priv::kOperate }, //
{ ResumeRequest::Id, {}, Priv::kOperate } //
}));
}

if (HasFeature(Feature::kForecastAdjustment))
{
VerifyOrExit(callback(ModifyForecastRequest::Id, context) == Loop::Continue, /**/);
ReturnErrorOnFailure(builder.Append({ ModifyForecastRequest::Id, {}, Priv::kOperate }));
}

if (HasFeature(Feature::kConstraintBasedAdjustment))
{
VerifyOrExit(callback(RequestConstraintBasedForecast::Id, context) == Loop::Continue, /**/);
ReturnErrorOnFailure(builder.Append({ RequestConstraintBasedForecast::Id, {}, Priv::kOperate }));
}

if (HasFeature(Feature::kStartTimeAdjustment) || HasFeature(Feature::kForecastAdjustment) ||
HasFeature(Feature::kConstraintBasedAdjustment))
{
VerifyOrExit(callback(CancelRequest::Id, context) == Loop::Continue, /**/);
ReturnErrorOnFailure(builder.Append({ CancelRequest::Id, {}, Priv::kOperate }));
}

exit:
return CHIP_NO_ERROR;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ class Instance : public AttributeAccessInterface, public CommandHandlerInterface

// CommandHandlerInterface
void InvokeCommand(HandlerContext & handlerContext) override;
CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context) override;
CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster,
DataModel::ListBuilder<DataModel::AcceptedCommandEntry> & builder) override;

Protocols::InteractionModel::Status CheckOptOutAllowsRequest(AdjustmentCauseEnum adjustmentCause);
void HandlePowerAdjustRequest(HandlerContext & ctx, const Commands::PowerAdjustRequest::DecodableType & commandData);
Expand Down
36 changes: 16 additions & 20 deletions src/app/clusters/energy-evse-server/energy-evse-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,41 +182,37 @@ CHIP_ERROR Instance::Write(const ConcreteDataAttributePath & aPath, AttributeVal
}

// CommandHandlerInterface
CHIP_ERROR Instance::EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
CHIP_ERROR Instance::EnumerateAcceptedCommands(const ConcreteClusterPath & cluster,
DataModel::ListBuilder<DataModel::AcceptedCommandEntry> & builder)
{
using namespace Commands;
using QF = DataModel::CommandQualityFlags;
using Priv = Access::Privilege;
ReturnErrorOnFailure(builder.EnsureAppendCapacity(7));

for (auto && cmd : {
Disable::Id,
EnableCharging::Id,
})
{
VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/);
}
ReturnErrorOnFailure(builder.AppendElements({
{ Disable::Id, QF::kTimed, Priv::kOperate }, //
{ EnableCharging::Id, QF::kTimed, Priv::kOperate }, //
}));

if (HasFeature(Feature::kV2x))
{
VerifyOrExit(callback(EnableDischarging::Id, context) == Loop::Continue, /**/);
ReturnErrorOnFailure(builder.Append({ EnableDischarging::Id, QF::kTimed, Priv::kOperate }));
}

if (HasFeature(Feature::kChargingPreferences))
{
for (auto && cmd : {
SetTargets::Id,
GetTargets::Id,
ClearTargets::Id,
})
{
VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/);
}
ReturnErrorOnFailure(builder.AppendElements({
{ SetTargets::Id, QF::kTimed, Priv::kOperate }, //
{ GetTargets::Id, QF::kTimed, Priv::kOperate }, //
{ ClearTargets::Id, QF::kTimed, Priv::kOperate }, //
}));
}

if (SupportsOptCmd(OptionalCommands::kSupportsStartDiagnostics))
{
callback(StartDiagnostics::Id, context);
ReturnErrorOnFailure(builder.Append({ StartDiagnostics::Id, QF::kTimed, Priv::kOperate }));
}

exit:
return CHIP_NO_ERROR;
}

Expand Down
3 changes: 2 additions & 1 deletion src/app/clusters/energy-evse-server/energy-evse-server.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ class Instance : public AttributeAccessInterface, public CommandHandlerInterface

// CommandHandlerInterface
void InvokeCommand(HandlerContext & handlerContext) override;
CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context) override;
CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster,
DataModel::ListBuilder<DataModel::AcceptedCommandEntry> & builder) override;

void HandleDisable(HandlerContext & ctx, const Commands::Disable::DecodableType & commandData);
void HandleEnableCharging(HandlerContext & ctx, const Commands::EnableCharging::DecodableType & commandData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
*
*/

#include "app/data-model-provider/MetadataList.h"
#include <app-common/zap-generated/attributes/Accessors.h>
#include <app/AttributeAccessInterfaceRegistry.h>
#include <app/CommandHandlerInterfaceRegistry.h>
#include <app/ConcreteClusterPath.h>
#include <app/InteractionModelEngine.h>
#include <app/clusters/microwave-oven-control-server/microwave-oven-control-server.h>
#include <app/clusters/mode-base-server/mode-base-server.h>
#include <app/data-model-provider/MetadataList.h>
#include <app/data-model-provider/MetadataTypes.h>
#include <app/reporting/reporting.h>
#include <app/util/attribute-storage.h>
Expand Down
60 changes: 26 additions & 34 deletions src/app/clusters/network-commissioning/network-commissioning.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1360,64 +1360,56 @@ void Instance::OnFailSafeTimerExpired()
}
}

CHIP_ERROR Instance::EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
CHIP_ERROR Instance::EnumerateAcceptedCommands(const ConcreteClusterPath & cluster,
DataModel::ListBuilder<DataModel::AcceptedCommandEntry> & builder)
{
using namespace Clusters::NetworkCommissioning::Commands;
using Priv = Access::Privilege;

if (mFeatureFlags.Has(Feature::kThreadNetworkInterface))
{
for (auto && cmd : {
ScanNetworks::Id,
AddOrUpdateThreadNetwork::Id,
RemoveNetwork::Id,
ConnectNetwork::Id,
ReorderNetwork::Id,
})
{
VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/);
}
}
else if (mFeatureFlags.Has(Feature::kWiFiNetworkInterface))
static constexpr size_t kNetworkCommands = 7; // Count of max possible commands assuming all features
ReturnErrorOnFailure(builder.EnsureAppendCapacity(kNetworkCommands));

bool hasThread = mFeatureFlags.Has(Feature::kThreadNetworkInterface);
bool hasWifi = mFeatureFlags.Has(Feature::kWiFiNetworkInterface);

if (hasThread || hasWifi)
{
for (auto && cmd : {
ScanNetworks::Id,
AddOrUpdateWiFiNetwork::Id,
RemoveNetwork::Id,
ConnectNetwork::Id,
ReorderNetwork::Id,
})
{
VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/);
}
ReturnErrorOnFailure(builder.AppendElements({
{ ScanNetworks::Id, {}, Priv::kAdminister }, //
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that all this code has to duplicate parts of the spec is really fragile.

What we should be doing is that cluster-objects codegen should be code-generating the relevant bits: either a getter for the AcceptedCommandEntry or at least the privileges/flags bits that can then be used to construct one.

I'm OK with this happening in a followup that then updates all these callsites, or in a PR that happens before this PR, either way. Just not as part of this PR.

But we don't want to be shipping this in the state this PR is creating, where all sorts of spec stuff is duplicated in the cluster impls.

Changes requested until we have a concrete plan for how this is going to work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the logic issues may be pre-existing as we still controlled IDs within code.
Is the worry that we may make mistakes in setting correct flags/privileges for these items?

i.e. are you asking for something like ScanNetworks::Entry to be generated by cluster-objects so that we do not make mistakes in flags and privileges?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the worry that we may make mistakes in setting correct flags/privileges for these items?

Yes, 100%. As this PR demonstrates, in several places. ;)

i.e. are you asking for something like ScanNetworks::Entry to be generated by cluster-objects so that we do not make mistakes in flags and privileges?

Yes.

{ RemoveNetwork::Id, {}, Priv::kAdminister }, //
{ ConnectNetwork::Id, {}, Priv::kAdminister }, //
{ ReorderNetwork::Id, {}, Priv::kAdminister }, //
}));
ReturnErrorOnFailure(
builder.Append({ hasThread ? AddOrUpdateThreadNetwork::Id : AddOrUpdateWiFiNetwork::Id, {}, Priv::kAdminister }));
}

if (mFeatureFlags.Has(Feature::kPerDeviceCredentials))
{
VerifyOrExit(callback(QueryIdentity::Id, context) == Loop::Continue, /**/);
ReturnErrorOnFailure(builder.Append({ QueryIdentity::Id, {}, Priv::kAdminister }));
}

exit:
return CHIP_NO_ERROR;
}

CHIP_ERROR Instance::EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
CHIP_ERROR Instance::EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, DataModel::ListBuilder<CommandId> & builder)
{
using namespace Clusters::NetworkCommissioning::Commands;

static constexpr size_t kCommands = 4; // Count of max possible commands assuming all features
ReturnErrorOnFailure(builder.EnsureAppendCapacity(kCommands));

if (mFeatureFlags.HasAny(Feature::kWiFiNetworkInterface, Feature::kThreadNetworkInterface))
{
for (auto && cmd : { ScanNetworksResponse::Id, NetworkConfigResponse::Id, ConnectNetworkResponse::Id })
{
VerifyOrExit(callback(cmd, context) == Loop::Continue, /**/);
}
ReturnErrorOnFailure(
builder.AppendElements({ ScanNetworksResponse::Id, NetworkConfigResponse::Id, ConnectNetworkResponse::Id }));
}

if (mFeatureFlags.Has(Feature::kPerDeviceCredentials))
{
VerifyOrExit(callback(QueryIdentityResponse::Id, context) == Loop::Continue, /**/);
ReturnErrorOnFailure(builder.Append(QueryIdentityResponse::Id));
}

exit:
return CHIP_NO_ERROR;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ class Instance : public CommandHandlerInterface,

// CommandHandlerInterface
void InvokeCommand(HandlerContext & ctx) override;
CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context) override;
CHIP_ERROR EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context) override;
CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster,
DataModel::ListBuilder<DataModel::AcceptedCommandEntry> & builder) override;
CHIP_ERROR EnumerateGeneratedCommands(const ConcreteClusterPath & cluster,
DataModel::ListBuilder<CommandId> & builder) override;

// AttributeAccessInterface
CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
Expand Down
Loading
Loading