Skip to content

Commit 1534122

Browse files
bzbarsky-applepull[bot]
authored andcommitted
Codegen a ResponseType for command cluster objects and use it to simplify templates (#11624)
* Output a ResponseType for command Type structs. * Use ResponseType in our InvokeCommand templates.
1 parent b6895b8 commit 1534122

File tree

18 files changed

+4407
-1732
lines changed

18 files changed

+4407
-1732
lines changed

examples/chip-tool/templates/partials/test_cluster.zapt

+4-5
Original file line numberDiff line numberDiff line change
@@ -141,22 +141,21 @@ class {{filename}}: public TestCommand
141141
cluster.Associate(mDevice, {{endpoint}});
142142

143143
{{#if isCommand}}
144-
using requestType = chip::app::Clusters::{{asUpperCamelCase cluster}}::Commands::{{asUpperCamelCase command}}::Type;
145-
using responseType = chip::app::{{chip_tests_item_response_type}};
144+
using RequestType = chip::app::Clusters::{{asUpperCamelCase cluster}}::Commands::{{asUpperCamelCase command}}::Type;
146145

147-
chip::app::Clusters::{{asUpperCamelCase cluster}}::Commands::{{asUpperCamelCase command}}::Type request;
146+
RequestType request;
148147
{{#chip_tests_item_parameters}}
149148
{{>commandValue ns=parent.cluster container=(concat "request." (asLowerCamelCase label)) definedValue=definedValue}}
150149
{{/chip_tests_item_parameters}}
151150

152-
auto success = [](void * context, const responseType & data) {
151+
auto success = [](void * context, const typename RequestType::ResponseType & data) {
153152
(static_cast<{{filename}} *>(context))->OnSuccessResponse_{{index}}({{#chip_tests_item_response_parameters}}{{#not_first}}, {{/not_first}}data.{{asLowerCamelCase name}}{{/chip_tests_item_response_parameters}});
154153
};
155154

156155
auto failure = [](void * context, EmberAfStatus status) {
157156
(static_cast<{{filename}} *>(context))->OnFailureResponse_{{index}}(status);
158157
};
159-
{{#if async}}ReturnErrorOnFailure({{else}}return {{/if}}cluster.InvokeCommand<requestType, responseType>(request, this, success, failure){{#if async}}){{/if}};
158+
{{#if async}}ReturnErrorOnFailure({{else}}return {{/if}}cluster.InvokeCommand(request, this, success, failure){{#if async}}){{/if}};
160159
{{else}}
161160
{{#chip_tests_item_parameters}}
162161
{{zapTypeToEncodableClusterObjectType type ns=parent.cluster}} {{asLowerCamelCase name}}Argument;

src/app/zap-templates/common/ClusterTestGeneration.js

-15
Original file line numberDiff line numberDiff line change
@@ -453,20 +453,6 @@ function isTestOnlyCluster(name)
453453
return testOnlyClusters.includes(name);
454454
}
455455

456-
function chip_tests_item_response_type(options)
457-
{
458-
const promise = assertCommandOrAttribute(this).then(item => {
459-
if (item.hasSpecificResponse) {
460-
return 'Clusters::' + asUpperCamelCase(this.cluster) + '::Commands::' + asUpperCamelCase(item.response.name)
461-
+ '::DecodableType';
462-
}
463-
464-
return 'DataModel::NullObjectType';
465-
});
466-
467-
return asPromise.call(this, promise, options);
468-
}
469-
470456
// test_cluster_command_value and test_cluster_value-equals are recursive partials using #each. At some point the |global|
471457
// context is lost and it fails. Make sure to attach the global context as a property of the | value |
472458
// that is evaluated.
@@ -602,7 +588,6 @@ function expectedValueHasProp(value, name)
602588
exports.chip_tests = chip_tests;
603589
exports.chip_tests_items = chip_tests_items;
604590
exports.chip_tests_item_parameters = chip_tests_item_parameters;
605-
exports.chip_tests_item_response_type = chip_tests_item_response_type;
606591
exports.chip_tests_item_response_parameters = chip_tests_item_response_parameters;
607592
exports.chip_tests_pics = chip_tests_pics;
608593
exports.isTestOnlyCluster = isTestOnlyCluster;

src/app/zap-templates/templates/app/CHIPClustersInvoke-src.zapt

+6-7
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,26 @@ namespace Controller {
1818

1919
{{#chip_cluster_commands}}
2020
{{#*inline "requestType"}}chip::app::Clusters::{{asUpperCamelCase parent.name}}::Commands::{{asUpperCamelCase name}}::Type{{/inline}}
21-
{{#*inline "responseType"}}chip::app::{{#if hasSpecificResponse}}Clusters::{{asUpperCamelCase parent.name}}::Commands::{{asUpperCamelCase response.name}}::DecodableType{{else}}DataModel::NullObjectType{{/if}}{{/inline}}
22-
template CHIP_ERROR ClusterBase::InvokeCommand<{{>requestType}}, {{>responseType}}>(const {{>requestType}} &, void *, CommandResponseSuccessCallback<{{>responseType}}>, CommandResponseFailureCallback);
21+
template CHIP_ERROR ClusterBase::InvokeCommand<{{>requestType}}>(const {{>requestType}} &, void *, CommandResponseSuccessCallback<typename {{>requestType}}::ResponseType>, CommandResponseFailureCallback);
2322
{{/chip_cluster_commands}}
2423
{{/chip_client_clusters}}
2524

26-
template <typename RequestDataT, typename ResponseDataT>
25+
template <typename RequestDataT>
2726
CHIP_ERROR ClusterBase::InvokeCommand(const RequestDataT & requestData, void * context,
28-
CommandResponseSuccessCallback<ResponseDataT> successCb, CommandResponseFailureCallback failureCb)
27+
CommandResponseSuccessCallback<typename RequestDataT::ResponseType> successCb, CommandResponseFailureCallback failureCb)
2928
{
3029
VerifyOrReturnError(mDevice != nullptr, CHIP_ERROR_INCORRECT_STATE);
3130

32-
auto onSuccessCb = [context, successCb](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, const ResponseDataT & responseData) {
31+
auto onSuccessCb = [context, successCb](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, const typename RequestDataT::ResponseType & responseData) {
3332
successCb(context, responseData);
3433
};
3534

3635
auto onFailureCb = [context, failureCb](const app::StatusIB & aStatus, CHIP_ERROR aError) {
3736
failureCb(context, app::ToEmberAfStatus(aStatus.mStatus));
3837
};
3938

40-
return InvokeCommandRequest<ResponseDataT>(mDevice->GetExchangeManager(), mDevice->GetSecureSession().Value(), mEndpoint,
41-
requestData, onSuccessCb, onFailureCb);
39+
return InvokeCommandRequest(mDevice->GetExchangeManager(), mDevice->GetSecureSession().Value(), mEndpoint,
40+
requestData, onSuccessCb, onFailureCb);
4241
};
4342

4443
} // namespace Controller

src/app/zap-templates/templates/app/cluster-objects.zapt

+24
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <app/data-model/Decode.h>
77
#include <app/data-model/Encode.h>
88
#include <app/data-model/List.h>
9+
#include <app/data-model/NullObject.h>
910
#include <app/EventLoggingTypes.h>
1011
#include <app/util/basic-types.h>
1112
#include <lib/support/BitFlags.h>
@@ -87,6 +88,22 @@ namespace {{asUpperCamelCase name}} {
8788
{{/last}}
8889
{{/zcl_structs}}
8990

91+
{{#zcl_commands}}
92+
{{#first}}
93+
namespace Commands {
94+
// Forward-declarations so we can reference these later.
95+
{{/first}}
96+
97+
namespace {{asUpperCamelCase name}} {
98+
struct Type;
99+
struct DecodableType;
100+
} // namespace {{asUpperCamelCase name}}
101+
{{#last}}
102+
103+
} // namespace Commands
104+
{{/last}}
105+
{{/zcl_commands}}
106+
90107
{{#zcl_commands}}
91108
{{#first}}
92109
namespace Commands {
@@ -110,6 +127,13 @@ public:
110127
{{/zcl_command_arguments}}
111128

112129
CHIP_ERROR Encode(TLV::TLVWriter &writer, TLV::Tag tag) const;
130+
131+
using ResponseType =
132+
{{~#if responseRef}}
133+
Clusters::{{asUpperCamelCase parent.name}}::Commands::{{getResponseCommandName responseRef}}::DecodableType;
134+
{{else}}
135+
DataModel::NullObjectType;
136+
{{/if}}
113137
};
114138

115139
struct DecodableType {

src/app/zap-templates/templates/app/helper.js

+10
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
const zapPath = '../../../../../third_party/zap/repo/dist/src-electron/';
2020
const templateUtil = require(zapPath + 'generator/template-util.js')
2121
const zclHelper = require(zapPath + 'generator/helper-zcl.js')
22+
const queryCommand = require(zapPath + 'db/query-command.js')
2223
const zclQuery = require(zapPath + 'db/query-zcl.js')
2324
const cHelper = require(zapPath + 'generator/helper-c.js')
2425
const string = require(zapPath + 'util/string.js')
@@ -476,6 +477,14 @@ function zapTypeToPythonClusterObjectType(type, options)
476477
return templateUtil.templatePromise(this.global, promise)
477478
}
478479

480+
async function getResponseCommandName(responseRef, options)
481+
{
482+
let pkgId = await templateUtil.ensureZclPackageId(this);
483+
484+
const { db, sessionId } = this.global;
485+
return queryCommand.selectCommandById(db, responseRef, pkgId).then(response => asUpperCamelCase(response.name));
486+
}
487+
479488
//
480489
// Module exports
481490
//
@@ -491,3 +500,4 @@ exports.asMEI = asMEI;
491500
exports.zapTypeToEncodableClusterObjectType = zapTypeToEncodableClusterObjectType;
492501
exports.zapTypeToDecodableClusterObjectType = zapTypeToDecodableClusterObjectType;
493502
exports.zapTypeToPythonClusterObjectType = zapTypeToPythonClusterObjectType;
503+
exports.getResponseCommandName = getResponseCommandName;

src/controller/CHIPCluster.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ class DLL_EXPORT ClusterBase
6262
* Success and Failure callbacks must be passed in through which the decoded response is provided as well as notification of any
6363
* failure.
6464
*/
65-
template <typename RequestDataT, typename ResponseDataT>
65+
template <typename RequestDataT>
6666
CHIP_ERROR InvokeCommand(const RequestDataT & requestData, void * context,
67-
CommandResponseSuccessCallback<ResponseDataT> successCb, CommandResponseFailureCallback failureCb);
67+
CommandResponseSuccessCallback<typename RequestDataT::ResponseType> successCb,
68+
CommandResponseFailureCallback failureCb);
6869

6970
/**
7071
* Functions for writing attributes. We have lots of different

src/controller/InvokeInteraction.h

+11-9
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,31 @@ namespace Controller {
3030
* the provided success callback or calls the provided failure callback.
3131
*
3232
* The RequestObjectT is generally expected to be a ClusterName::Commands::CommandName::Type struct, but any object
33-
* that can be encoded using the DataModel::Encode machinery and exposes the GetClusterId() and GetCommandId() functions
33+
* that can be encoded using the DataModel::Encode machinery and exposes the
34+
* GetClusterId() and GetCommandId() functions and a ResponseType type
3435
* is expected to work.
3536
*
36-
* The ResponseObjectT is expected to be one of two things:
37+
* The ResponseType is expected to be one of two things:
3738
*
3839
* - If a data response is expected on success, a struct type decodable via DataModel::Decode which has GetClusterId() and
3940
* GetCommandId() methods. A ClusterName::Commands::ResponseCommandName::DecodableType is typically used.
40-
* - If a status response is expected on success, a DataModel::NullObjectType.
41+
* - If a status response is expected on success, DataModel::NullObjectType.
4142
*
4243
*/
43-
template <typename ResponseObjectT = app::DataModel::NullObjectType, typename RequestObjectT>
44-
CHIP_ERROR InvokeCommandRequest(Messaging::ExchangeManager * aExchangeMgr, SessionHandle sessionHandle, chip::EndpointId endpointId,
45-
const RequestObjectT & requestCommandData,
46-
typename TypedCommandCallback<ResponseObjectT>::OnSuccessCallbackType onSuccessCb,
47-
typename TypedCommandCallback<ResponseObjectT>::OnErrorCallbackType onErrorCb)
44+
template <typename RequestObjectT>
45+
CHIP_ERROR
46+
InvokeCommandRequest(Messaging::ExchangeManager * aExchangeMgr, SessionHandle sessionHandle, chip::EndpointId endpointId,
47+
const RequestObjectT & requestCommandData,
48+
typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnSuccessCallbackType onSuccessCb,
49+
typename TypedCommandCallback<typename RequestObjectT::ResponseType>::OnErrorCallbackType onErrorCb)
4850
{
4951
app::CommandPathParams commandPath = { endpointId, 0, RequestObjectT::GetClusterId(), RequestObjectT::GetCommandId(),
5052
(app::CommandPathFlags::kEndpointIdValid) };
5153

5254
//
5355
// Let's create a handle version of the decoder to ensure we do correct clean-up of it if things go south at any point below
5456
//
55-
auto decoder = chip::Platform::MakeUnique<TypedCommandCallback<ResponseObjectT>>(onSuccessCb, onErrorCb);
57+
auto decoder = chip::Platform::MakeUnique<TypedCommandCallback<typename RequestObjectT::ResponseType>>(onSuccessCb, onErrorCb);
5658
VerifyOrReturnError(decoder != nullptr, CHIP_ERROR_NO_MEMORY);
5759

5860
//

src/controller/tests/TestServerCommandDispatch.cpp

+14-6
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,18 @@ class TestCommandInteraction
114114
private:
115115
};
116116

117+
// We want to send a TestSimpleArgumentRequest::Type, but get a
118+
// TestStructArrayArgumentResponse in return, so need to shadow the actual
119+
// ResponseType that TestSimpleArgumentRequest has.
120+
struct FakeRequest : public TestCluster::Commands::TestSimpleArgumentRequest::Type
121+
{
122+
using ResponseType = TestCluster::Commands::TestStructArrayArgumentResponse::DecodableType;
123+
};
124+
117125
void TestCommandInteraction::TestNoHandler(nlTestSuite * apSuite, void * apContext)
118126
{
119127
TestContext & ctx = *static_cast<TestContext *>(apContext);
120-
TestCluster::Commands::TestSimpleArgumentRequest::Type request;
128+
FakeRequest request;
121129
auto sessionHandle = ctx.GetSessionBobToAlice();
122130

123131
request.arg1 = true;
@@ -142,8 +150,8 @@ void TestCommandInteraction::TestNoHandler(nlTestSuite * apSuite, void * apConte
142150

143151
ctx.EnableAsyncDispatch();
144152

145-
chip::Controller::InvokeCommandRequest<TestCluster::Commands::TestStructArrayArgumentResponse::DecodableType>(
146-
&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, request, onSuccessCb, onFailureCb);
153+
chip::Controller::InvokeCommandRequest(&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, request, onSuccessCb,
154+
onFailureCb);
147155

148156
ctx.DrainAndServiceIO();
149157

@@ -176,7 +184,7 @@ DECLARE_DYNAMIC_ENDPOINT(testEndpoint, testEndpointClusters);
176184
void TestCommandInteraction::TestDataResponse(nlTestSuite * apSuite, void * apContext)
177185
{
178186
TestContext & ctx = *static_cast<TestContext *>(apContext);
179-
TestCluster::Commands::TestSimpleArgumentRequest::Type request;
187+
FakeRequest request;
180188
auto sessionHandle = ctx.GetSessionBobToAlice();
181189
TestClusterCommandHandler commandHandler;
182190

@@ -220,8 +228,8 @@ void TestCommandInteraction::TestDataResponse(nlTestSuite * apSuite, void * apCo
220228

221229
responseDirective = kSendDataResponse;
222230

223-
chip::Controller::InvokeCommandRequest<TestCluster::Commands::TestStructArrayArgumentResponse::DecodableType>(
224-
&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, request, onSuccessCb, onFailureCb);
231+
chip::Controller::InvokeCommandRequest(&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, request, onSuccessCb,
232+
onFailureCb);
225233

226234
ctx.DrainAndServiceIO();
227235

src/controller/tests/data_model/TestCommands.cpp

+11-3
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,15 @@ class TestCommandInteraction
159159
void TestCommandInteraction::TestDataResponse(nlTestSuite * apSuite, void * apContext)
160160
{
161161
TestContext & ctx = *static_cast<TestContext *>(apContext);
162-
TestCluster::Commands::TestSimpleArgumentRequest::Type request;
162+
// We want to send a TestSimpleArgumentRequest::Type, but get a
163+
// TestStructArrayArgumentResponse in return, so need to shadow the actual
164+
// ResponseType that TestSimpleArgumentRequest has.
165+
struct FakeRequest : public TestCluster::Commands::TestSimpleArgumentRequest::Type
166+
{
167+
using ResponseType = TestCluster::Commands::TestStructArrayArgumentResponse::DecodableType;
168+
};
169+
170+
FakeRequest request;
163171
auto sessionHandle = ctx.GetSessionBobToAlice();
164172

165173
bool onSuccessWasCalled = false;
@@ -196,8 +204,8 @@ void TestCommandInteraction::TestDataResponse(nlTestSuite * apSuite, void * apCo
196204

197205
responseDirective = kSendDataResponse;
198206

199-
chip::Controller::InvokeCommandRequest<TestCluster::Commands::TestStructArrayArgumentResponse::DecodableType>(
200-
&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, request, onSuccessCb, onFailureCb);
207+
chip::Controller::InvokeCommandRequest(&ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, request, onSuccessCb,
208+
onFailureCb);
201209

202210
ctx.DrainAndServiceIO();
203211

0 commit comments

Comments
 (0)