blob: b6f845abaa937ee6a309cc2b4a6108dbe2b30975 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 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.
*/
// Import helpers from zap core
const zapPath = '../../../../../third_party/zap/repo/dist/src-electron/';
const templateUtil = require(zapPath + 'generator/template-util.js');
const zclHelper = require(zapPath + 'generator/helper-zcl.js');
const iteratorUtil = require(zapPath + 'util/iterator-util.js');
const { asBlocks, ensureClusters } = require('../../common/ClustersHelper.js');
const StringHelper = require('../../common/StringHelper.js');
const ChipTypesHelper = require('../../common/ChipTypesHelper.js');
function throwErrorIfUndefined(item, errorMsg, conditions)
{
conditions.forEach(condition => {
if (condition == undefined) {
console.log(item);
console.log(errorMsg);
throw error;
}
});
}
function checkIsInsideClusterBlock(context, name)
{
const clusterName = context.name;
const clusterSide = context.side;
const errorMsg = name + ': Not inside a ({#chip_server_clusters}} block.';
throwErrorIfUndefined(context, errorMsg, [ clusterName, clusterSide ]);
return { clusterName, clusterSide };
}
function checkIsInsideCommandBlock(context, name)
{
const clusterName = context.clusterName;
const clusterSide = context.clusterSide;
const commandId = context.id;
const errorMsg = name + ': Not inside a ({#chip_cluster_commands}} block.';
throwErrorIfUndefined(context, errorMsg, [ commandId, clusterName, clusterSide ]);
return commandId;
}
function checkIsInsideAttributeBlock(context, name)
{
const code = context.code;
const errorMsg = name + ': Not inside a ({#chip_server_attributes}} block.';
throwErrorIfUndefined(context, errorMsg, [ code ]);
}
function checkIsChipType(context, name)
{
const type = context.chipType;
const errorMsg = name + ': Could not find chipType';
throwErrorIfUndefined(context, errorMsg, [ type ]);
return type;
}
function getCommands(methodName)
{
const { clusterName, clusterSide } = checkIsInsideClusterBlock(this, methodName);
return clusterSide == 'client' ? ensureClusters(this).getClientCommands(clusterName)
: ensureClusters(this).getServerCommands(clusterName);
}
function getAttributes(methodName)
{
const { clusterName, clusterSide } = checkIsInsideClusterBlock(this, methodName);
return ensureClusters(this).getAttributesByClusterName(clusterName);
}
function getResponses(methodName)
{
const { clusterName, clusterSide } = checkIsInsideClusterBlock(this, methodName);
return clusterSide == 'client' ? ensureClusters(this).getClientResponses(clusterName)
: ensureClusters(this).getServerResponses(clusterName);
}
/**
* Creates block iterator over the enabled server side clusters
*
* @param {*} options
*/
function chip_server_clusters(options)
{
return asBlocks.call(this, ensureClusters(this).getServerClusters(), options);
}
/**
* Check if there is any enabled server clusters
*
*/
function chip_has_server_clusters(options)
{
return ensureClusters(this).getServerClusters().then(clusters => !!clusters.length);
}
/**
* Creates block iterator over client side enabled clusters
*
* @param {*} options
*/
function chip_client_clusters(options)
{
return asBlocks.call(this, ensureClusters(this).getClientClusters(), options);
}
/**
* Check if there is any enabled client clusters
*
*/
function chip_has_client_clusters(options)
{
return ensureClusters(this).getClientClusters().then(clusters => !!clusters.length);
}
/**
* Creates block iterator over enabled clusters
*
* @param {*} options
*/
function chip_clusters(options)
{
return asBlocks.call(this, ensureClusters(this).getClusters(), options);
}
/**
* Check if there is any enabled clusters
*
*/
function chip_has_clusters(options)
{
return ensureClusters(this).getClusters().then(clusters => !!clusters.length);
}
/**
* Creates block iterator over the server global responses
*
* @param {*} options
*/
function chip_server_global_responses(options)
{
return asBlocks.call(this, getServerGlobalAttributeResponses(this), options);
}
async function if_in_global_responses(options)
{
const attribute = this.response.arguments[0];
const globalResponses = await getServerGlobalAttributeResponses(this);
const responseTypeExists = globalResponses.find(
// Some fields of item/attribute here may be undefined.
item => item.isList == attribute.isList && item.isStruct == attribute.isStruct && item.chipType == attribute.chipType
&& item.isNullable == attribute.isNullable && item.isOptional == attribute.isOptional)
if (responseTypeExists)
{
return options.fn(this);
}
else
{
return options.inverse(this);
}
}
function getServerGlobalAttributeResponses(context)
{
const sorter = (a, b) => a.chipCallback.name.localeCompare(b.chipCallback.name, 'en', { numeric : true });
const reducer = (unique, item) => {
const { type, size, isList, isOptional, isNullable, chipCallback, chipType } = item.response.arguments[0];
// List-typed elements have a dedicated callback
if (isList) {
return unique;
}
if (unique.find(item => item.chipCallback.name == chipCallback.name)) {
return unique;
}
return [...unique, { chipCallback, chipType, size, isOptional, isNullable } ];
};
const filter = attributes => attributes.reduce(reducer, []).sort(sorter);
return ensureClusters(context).getAttributesByClusterSide('server').then(filter);
}
/**
* Creates block iterator over the cluster commands for a given cluster/side.
*
* This function is meant to be used inside a {{#chip_*_clusters}}
* block. It will throw otherwise.
*
* @param {*} options
*/
function chip_cluster_commands(options)
{
const commands = getCommands.call(this, 'chip_cluster_commands');
return asBlocks.call(this, commands, options);
}
/**
* Creates block iterator over the cluster attributes for a given cluster/side.
*
* This function is meant to be used inside a {{#chip_*_clusters}}
* block. It will throw otherwise.
*
* @param {*} options
*/
function chip_cluster_attributes(options)
{
const attributes = getAttributes.call(this, 'chip_cluster_attributes');
return asBlocks.call(this, attributes, options);
}
/**
* Creates block iterator over the cluster responses for a given cluster/side.
*
* This function is meant to be used inside a {{#chip_*_clusters}}
* block. It will throw otherwise.
*
* @param {*} options
*/
function chip_cluster_responses(options)
{
const responses = getResponses.call(this, 'chip_cluster_responses');
return asBlocks.call(this, responses, options);
}
/**
* Creates block iterator over the current command arguments for a given cluster/side.
*
* This function is meant to be used inside a {{#chip_cluster_commands}}
* block. It will throw otherwise.
*
* @param {*} options
*/
function chip_cluster_command_arguments(options)
{
const commandId = checkIsInsideCommandBlock(this, 'chip_cluster_command_arguments');
const commands = getCommands.call(this.parent, 'chip_cluster_commands_argments');
const filter = command => command.id == commandId;
return asBlocks.call(this,
commands.then(items => items.find(filter).arguments.map((value, index) => ({...value, fieldIdentifier : index }))), options);
}
/**
* Creates block iterator over the current command arguments for a given cluster/side.
*
* This function is meant to be used inside a {{#chip_cluster_commands}}
* block. It will throw otherwise.
*
* The arguments list built by this function differs from {{chip_cluster_command_arguments}}.
* For example, if a command contains a single struct argument "SomeStruct", with the following type:
*
* struct SomeStruct {
* uint8_t a;
* uint16_t b;
* uint32_t c;
* }
*
* then that argument will be expanded into 3 arguments (uint8_t a, uint16_t b, uint32_t c).
*
* @param {*} options
*/
function chip_cluster_command_arguments_with_structs_expanded(options)
{
const commandId = checkIsInsideCommandBlock(this, 'chip_cluster_command_arguments');
const commands = getCommands.call(this.parent, 'chip_cluster_commands_argments_with_structs_expanded');
const filter = command => command.id == commandId;
return asBlocks.call(this, commands.then(items => {
const item = items.find(filter);
return item.expandedArguments || item.arguments;
}),
options);
}
/**
* Creates block iterator over the current response arguments for a given cluster/side.
*
* This function is meant to be used inside a {{#chip_cluster_responses}}
* block. It will throw otherwise.
*
* @param {*} options
*/
function chip_cluster_response_arguments(options)
{
const commandId = checkIsInsideCommandBlock(this, 'chip_cluster_response_arguments');
const responses = getResponses.call(this.parent, 'chip_cluster_responses_argments');
const filter = command => command.id == commandId;
return asBlocks.call(this,
responses.then(items => items.find(filter).arguments.map((value, index) => ({...value, fieldIdentifier : index }))), options);
}
/**
* Returns if a given server cluster has any attributes of type List[T]
*
* This function is meant to be used inside a {{#chip_server_clusters}}
* block. It will throw otherwise.
*
* @param {*} options
*/
function chip_server_has_list_attributes(options)
{
const { clusterName } = checkIsInsideClusterBlock(this, 'chip_server_has_list_attributes');
const attributes = ensureClusters(this).getServerAttributes(clusterName);
const filter = attribute => attribute.isList;
return attributes.then(items => items.find(filter));
}
/**
* Returns if a given client cluster has any attributes of type List[T]
*
* This function is meant to be used inside a {{#chip_client_clusters}}
* block. It will throw otherwise.
*
* @param {*} options
*/
function chip_client_has_list_attributes(options)
{
const { clusterName } = checkIsInsideClusterBlock(this, 'chip_client_has_list_attributes');
const attributes = ensureClusters(this).getClientAttributes(clusterName);
const filter = attribute => attribute.isList;
return attributes.then(items => items.find(filter));
}
/**
* Creates block iterator over the server side cluster attributes
* for a given cluster.
*
* This function is meant to be used inside a {{#chip_server_clusters}}
* block. It will throw otherwise.
*
* @param {*} options
*/
function chip_server_cluster_attributes(options)
{
const { clusterName } = checkIsInsideClusterBlock(this, 'chip_server_cluster_attributes');
const attributes = ensureClusters(this).getServerAttributes(clusterName);
return asBlocks.call(this, attributes, options);
}
/**
* Creates block iterator over the server side cluster attributes
* for a given cluster.
*
* This function is meant to be used inside a {{#chip_server_clusters}}
* block. It will throw otherwise.
*
* @param {*} options
*/
function chip_server_cluster_events(options)
{
const { clusterName } = checkIsInsideClusterBlock(this, 'chip_server_cluster_events');
const events = ensureClusters(this).getServerEvents(clusterName);
return asBlocks.call(this, events, options);
}
function chip_attribute_list_entryTypes(options)
{
checkIsInsideAttributeBlock(this, 'chip_attribute_list_entry_types');
return templateUtil.collectBlocks(this.items, options, this);
}
/**
* Creates block iterator over commands for a given cluster that have the
* following properties:
*
* 1) Are not manufacturer-specific (to exclude MfgSpecificPing)
* 2) Are available in the isCommandAvailable sense.
*/
function chip_available_cluster_commands(options)
{
const { clusterName, clusterSide } = checkIsInsideClusterBlock(this, 'chip_available_cluster_commands');
let promise = iteratorUtil.all_user_cluster_commands_helper.call(this, options)
.then(endpointCommands => endpointCommands.filter(command => {
return command.clusterName == clusterName
&& zclHelper.isCommandAvailable(
clusterSide, command.incoming, command.outgoing, command.commandSource, command.name)
&& /* exclude MfgSpecificPing */ !command.mfgCode;
}))
.then(filteredCommands => templateUtil.collectBlocks(filteredCommands, options, this));
return promise;
}
/**
* Creates block iterator over structures belonging to the current cluster
*/
async function chip_cluster_specific_structs(options)
{
const { clusterName, clusterSide } = checkIsInsideClusterBlock(this, 'chip_cluster_specific_structs');
const structs = await ensureClusters(this).getStructuresByClusterName(clusterName);
return templateUtil.collectBlocks(structs, options, this);
}
/**
* Creates block iterator over structures that are shared between clusters
*/
async function chip_shared_structs(options)
{
const structs = await ensureClusters(this).getSharedStructs();
return templateUtil.collectBlocks(structs, options, this);
}
/**
* Checks whether a type is an enum for purposes of its chipType. That includes
* both spec-defined enum types and types that we map to enum types in our code.
*/
async function if_chip_enum(type, options)
{
if (type.toLowerCase() == 'vendor_id') {
return options.fn(this);
}
let pkgId = await templateUtil.ensureZclPackageId(this);
let checkResult = await zclHelper.isEnum(this.global.db, type, pkgId);
let result;
if (checkResult != 'unknown') {
result = options.fn(this);
} else {
result = options.inverse(this);
}
return templateUtil.templatePromise(this.global, result);
}
//
// Module exports
//
exports.chip_clusters = chip_clusters;
exports.chip_has_clusters = chip_has_clusters;
exports.chip_client_clusters = chip_client_clusters;
exports.chip_has_client_clusters = chip_has_client_clusters;
exports.chip_server_clusters = chip_server_clusters;
exports.chip_has_server_clusters = chip_has_server_clusters;
exports.chip_cluster_commands = chip_cluster_commands;
exports.chip_cluster_command_arguments = chip_cluster_command_arguments;
exports.chip_cluster_command_arguments_with_structs_expanded = chip_cluster_command_arguments_with_structs_expanded;
exports.chip_cluster_attributes = chip_cluster_attributes;
exports.chip_server_global_responses = chip_server_global_responses;
exports.chip_cluster_responses = chip_cluster_responses;
exports.chip_cluster_response_arguments = chip_cluster_response_arguments
exports.chip_attribute_list_entryTypes = chip_attribute_list_entryTypes;
exports.chip_server_cluster_attributes = chip_server_cluster_attributes;
exports.chip_server_cluster_events = chip_server_cluster_events;
exports.chip_server_has_list_attributes = chip_server_has_list_attributes;
exports.chip_available_cluster_commands = chip_available_cluster_commands;
exports.if_chip_enum = if_chip_enum;
exports.if_in_global_responses = if_in_global_responses;
exports.chip_cluster_specific_structs = chip_cluster_specific_structs;
exports.chip_shared_structs = chip_shared_structs;