Skip to content

Commit

Permalink
Update the ZAP template to encode/decode commands
Browse files Browse the repository at this point in the history
There are a few issues with the current template.
 1. {{isArray}} is not taken into account. It results into a mismatch between
    the signatures of some clusters methods
 2. Instead of using raw types, such as 'uint8_t', 'uint16_t', the current template
    uses 'enum'. It creates 2 types of issues:
     * The current template generates code that rely on sizeof(type) to move the
       pointer for the payload buffer. It does not work well with 'enum'.
     * The signatures of methods are using raw types. So it ask for a change
       in all the cluster signatures.
  3. The buffer start pointer is mispositioned: It uses 0 instead of cmd->payloadStartIndex
  4. String are using 'uint8_t *', which is OK but the pointer to the payload buffer is not
     moved to take into account the additional bytes with the size of the string
  • Loading branch information
vivien-apple committed Dec 1, 2020
1 parent c4aeea0 commit 4b99575
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 29 deletions.
4 changes: 0 additions & 4 deletions src/app/zap-templates/af-structs.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ typedef struct _{{asType label}} {
{{ident}}chip::EndpointId {{asSymbol label}};
{{else if (isStrEqual label "endpointId")}}
{{ident}}chip::EndpointId {{asSymbol label}};
{{else if (isStrEqual type "CLUSTER_ID")}}
{{ident}}chip::ClusterId {{asSymbol label}};
{{else if (isStrEqual type "ATTRIBUTE_ID")}}
{{ident}}chip::AttributeId {{asSymbol label}};
{{else if (isStrEqual label "groupId")}}
{{ident}}chip::GroupId {{asSymbol label}};
{{else if (isStrEqual label "commandId")}}
Expand Down
2 changes: 1 addition & 1 deletion src/app/zap-templates/attribute-size.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
// ZCL attribute sizes
{{#zcl_atomics}}
{{#if size}}
{{ident}}ZCL_{{asDelimitedMacro name}}_ATTRIBUTE_TYPE, {{size}}, \
{{ident}}ZCL_{{asDelimitedMacro name}}_ATTRIBUTE_TYPE, {{size}},
{{/if}}
{{/zcl_atomics}}
42 changes: 38 additions & 4 deletions src/app/zap-templates/call-command-handler-src.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,49 @@ EmberAfStatus emberAf{{asCamelCased name false}}Cluster{{asCamelCased side false
{{#if (isCommandAvailable parent.side incoming outgoing commandSource name)}}
case ZCL_{{asDelimitedMacro name}}_COMMAND_ID: {
{{#if (zcl_command_arguments_count this.id)}}
uint32_t argOffset = 0;
uint32_t payloadOffset = cmd->payloadStartIndex;
{{#zcl_command_arguments}}
{{asUnderlyingType type}} * {{asSymbol label}} = ({{asUnderlyingType type}} *)(cmd->buffer + argOffset);
{{#if (isNullable type isArray)}}
uint8_t * {{asSymbol label}};
{{else}}
{{asUnderlyingType type}} {{asSymbol label}};
{{/if}}
{{/zcl_command_arguments}}
{{#zcl_command_arguments}}
{{#if presentIf}}
if ({{presentIf}})
{
{{/if}}
{{#if isArray}}
{{asSymbol label}} = cmd->buffer + payloadOffset;
{{else}}
if (cmd->bufLen < payloadOffset + {{asTypeLength type}})
{
return EMBER_ZCL_STATUS_MALFORMED_COMMAND;
}
{{asSymbol label}} = emberAfGet{{asReadType type}}(cmd->buffer, payloadOffset, cmd->bufLen);
{{/if}}
{{#unless (isLastElement index count)}}
argOffset+= sizeof({{asUnderlyingType type}});
{{#if (isString type)}}
payloadOffset += emberAf{{asReadType type}}Length({{asSymbol label}}) + {{asTypeLength type}};
{{else}}
payloadOffset += {{asTypeLength type}};
{{/if}}
{{/unless}}
{{#if presentIf}}
}
else
{
{{#if isArray}}
{{asSymbol label}} = NULL;
{{else}}
{{asSymbol label}} = {{asValueIfNotPresent type}};
{{/if}}
}
{{/if}}
{{/zcl_command_arguments}}

wasHandled = emberAf{{asCamelCased parent.name false}}Cluster{{asCamelCased name false}}Callback({{#zcl_command_arguments}} *{{asSymbol label}}{{#unless (isLastElement index count)}}, {{/unless}}{{/zcl_command_arguments}});
wasHandled = emberAf{{asCamelCased parent.name false}}Cluster{{asCamelCased name false}}Callback({{#zcl_command_arguments}} {{asSymbol label}}{{#unless (isLastElement index count)}}, {{/unless}}{{/zcl_command_arguments}});
{{else}}
wasHandled = emberAf{{asCamelCased parent.name false}}Cluster{{asCamelCased name false}}Callback();
{{/if}}
Expand Down
2 changes: 1 addition & 1 deletion src/app/zap-templates/callback.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ void emberAf{{asCamelCased name false}}Cluster{{asCamelCased side false}}TickCal
*/

{{#if (zcl_command_arguments_count this.id)}}
bool emberAf{{asCamelCased parent.name false}}Cluster{{asCamelCased name false}}Callback({{#zcl_command_arguments}} {{asUnderlyingType type}} {{asSymbol label}}{{#unless (isLastElement index count)}}, {{/unless}}{{/zcl_command_arguments}});
bool emberAf{{asCamelCased parent.name false}}Cluster{{asCamelCased name false}}Callback({{#zcl_command_arguments}} {{#if isArray}}uint8_t * {{else}}{{asUnderlyingType type}}{{/if}} {{asSymbol label}}{{#unless (isLastElement index count)}}, {{/unless}}{{/zcl_command_arguments}});
{{else}}
bool emberAf{{asCamelCased parent.name false}}Cluster{{asCamelCased name false}}Callback();
{{/if}}
Expand Down
1 change: 1 addition & 0 deletions src/app/zap-templates/chip-templates.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "CHIP templates",
"version": "chip-v1",
"helpers": ["helper-chip.js"],
"override": "override.js",
"templates": [
{
"path": "af-structs.zapt",
Expand Down
117 changes: 98 additions & 19 deletions src/app/zap-templates/helper-chip.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,11 @@
* @module Templating API: toplevel utility helpers
*/

// Import Zcl helper from zap core
const helperZcl = require('../../../third_party/zap/repo/src-electron/generator/helper-zcl.js')

/**
* Dummy helper that add a string to the templates showing
* if the strings matches. Use to demonstrate the use
* of ZAP helper within the chip-helper environment
*
* @param {*} str1 : First string to compare
* @param {*} str2 : Second string to comapre
*/
function example_helper(str1, str2) {
if (helperZcl.isStrEqual(str1, str2)) {
return 'The two strings are identical'
} else {
return 'The two strings are different'
}
}
// Import helpers from zap core
const zapPath = '../../../third_party/zap/repo/src-electron/generator/';
const cHelper = require(zapPath + 'helper-c.js')
const zclHelper = require(zapPath + 'helper-zcl.js')
const templateUtil = require(zapPath + 'template-util.js')

/**
* Produces the top-of-the-file header for a C file.
Expand All @@ -65,10 +52,102 @@ function chip_header() {
*/`;
}

function isString(type) {
return type == 'CHAR_STRING' || type == 'OCTET_STRING' || type == 'LONG_CHAR_STRING' || type == 'LONG_OCTET_STRING';
}

function isNullable(type, isArray) { return isString(type) || isArray; }

function asValueIfNotPresent(type) {
var promise = templateUtil.ensureZclPackageId(this)
.then((packageId) => {
switch (type) {
case 'CHAR_STRING':
case 'OCTET_STRING':
return 'NULL'
case 'LONG_CHAR_STRING': case 'LONG_OCTET_STRING': return 'NULL' default: return cHelper.asUnderlyingType
.call(this, type)
.then(zclType => {
switch (zclType) {
case 'uint8_t':
return 'UINT8_MAX'
case 'uint16_t': return 'UINT16_MAX'
case 'uint32_t': return 'UINT32_MAX' default: error =
'Unhandled underlying type ' + zclType + ' for original type ' + type
throw error
}
})
}
})
.catch((err) => { console.log(err); });

return templateUtil.templatePromise(this.global, promise)
}
function asTypeLength(type) {
var promise = templateUtil.ensureZclPackageId(this)
.then((packageId) => {
switch (type) {
case 'CHAR_STRING':
case 'OCTET_STRING':
return '1u'
case 'LONG_CHAR_STRING': case 'LONG_OCTET_STRING': return '2u' default: return cHelper.asUnderlyingType
.call(this, type)
.then(zclType => {
switch (zclType) {
case 'int8_t':
case 'uint8_t':
return '1u'
case 'int16_t': case 'uint16_t': return '2u'
case 'int24_t': case 'uint24_t': return '3u'
case 'int32_t': case 'uint32_t': return '4u' default: error =
'Unhandled underlying type ' + zclType + ' for original type ' + type
throw error
}
})
}
})
.catch((err) => { console.log(err); });

return templateUtil.templatePromise(this.global, promise)
}

function asReadType(type) {
var promise =
templateUtil.ensureZclPackageId(this)
.then((packageId) => {
switch (type) {
case 'CHAR_STRING':
case 'OCTET_STRING':
return 'String'
case 'LONG_CHAR_STRING': case 'LONG_OCTET_STRING': return 'LongString' default: return cHelper.asUnderlyingType
.call(this, type)
.then(zclType => {
switch (zclType) {
case 'int8_t':
case 'uint8_t':
return 'Int8u'
case 'int16_t': case 'uint16_t': return 'Int16u'
case 'int24_t': case 'uint24_t': return 'Int24u'
case 'int32_t': case 'uint32_t': return 'Int32u' default: error =
'Unhandled underlying type ' + zclType + ' for original type ' + type
throw error
}
})
}
})
.catch((err) => { console.log(err); });

return templateUtil.templatePromise(this.global, promise)
}

// WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
//
// Note: these exports are public API. Templates that might have been created in the past and are
// available in the wild might depend on these names.
// If you rename the functions, you need to still maintain old exports list.
exports.chip_header = chip_header;
exports.example_helper = example_helper;
exports.isString = isString;
exports.isNullable = isNullable;
exports.asReadType = asReadType;
exports.asTypeLength = asTypeLength;
exports.asValueIfNotPresent = asValueIfNotPresent;
49 changes: 49 additions & 0 deletions src/app/zap-templates/override.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
*
* Copyright (c) 2020 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

function atomicType(arg) {
switch (arg.name) {
case 'attribute_id':
return 'chip::AttributeId';
case 'cluster_id':
return 'chip::ClusterId';
default:
throw 'not overriding';
}
}

function nonAtomicType(arg) {
switch (arg.name) {
case 'Status':
case 'IasEnrollResponseCode':
case 'DoorLockUserStatus':
case 'DoorLockUserType':
case 'StepMode':
case 'HueStepMode':
case 'SaturationStepMode':
case 'MoveMode':
case 'HueMoveMode':
case 'SaturationMoveMode':
case 'HueDirection':
return 'uint8_t';
default:
throw 'not overriding';
}
}

exports.atomicType = atomicType
exports.nonAtomicType = nonAtomicType

0 comments on commit 4b99575

Please sign in to comment.