From db03a4feda2b54074825a47e6a72dbe89cfb7707 Mon Sep 17 00:00:00 2001 From: Bharat Dandu Date: Thu, 19 Jan 2023 13:03:51 -0500 Subject: [PATCH] Adding to enabled_attributes_for_cluster_and_side and zcl_attributes_server helper to replace chip_server_cluster_attributes - chip_server_cluster_attributes is returning server attributes which are not actually enabled because the server side cluster is disabled. - The reason chip_server_cluster_attributes is picking the server side attributes which are not enabled is because those are enabled in the .zap file(Which is the case for saving user selections and ease of use). However the helper is not actually checking if the cluster is enabled as well. - enabled_attributes_for_cluster_and_side is solving this issue correctly and actually only showing attributes which are truly enabled. - Extending endpointTypeAttributeExtended such that there is a common place for an attribute map - Updating endpointTypeAttributeExtended with everything that attributeExportMapping had such that it can act as a common place for all other temporary maps that have been created across our code. Also sorting endpointTypeAttributeExtended for easier readability and avoiding duplicate keys - Updating zcl_attributes_server block helper such that it can behave like chip_server_cluster_attributes. - Updating attribute map such that it can be used more widely - Adding additional helpers to remove the stateful helpers - Adding as_underlying_language_specific_zcl_type to return language specific data types for zcl data types - Adding removeKeys option for removing certain columns which are not needed in a block helper such as enabled_attributes_for_cluster_and_side and zcl_attributes_server - Adding if_unsupported_attribute_type and if_attribute_complex to check for unsupported and complex zcl data types - Deprecating old stateful helpers with new stateless helpers - Adding select data type using type name and cluster id to data_types, enums, bitmap, numbers and structs - Adding if_unsupported_attribute_callback helper and if_basic_attribute helpers if helpers - Deprecating java and python zcl type helpers with new zcl type helpers such that they do not need any stateful information coming from parent block helpers. For eg chipType and chipCallback.type - Github: ZAP#898 --- src-electron/db/db-mapping.js | 77 +++++--- src-electron/db/query-attribute.js | 86 ++++++--- src-electron/db/query-bitmap.js | 59 ++++++ src-electron/db/query-data-type.js | 78 ++++++++ src-electron/db/query-enum.js | 68 +++++++ src-electron/db/query-number.js | 58 ++++++ src-electron/db/query-struct.js | 66 +++++++ src-electron/db/query-zcl.js | 9 + src-electron/generator/helper-attribute.js | 92 +++++++++ src-electron/generator/helper-session.js | 37 +++- src-electron/generator/helper-zcl.js | 180 +++++++++++++++--- .../zap-templates/templates/chip/helper.js | 13 +- .../controller/java/templates/helper.js | 92 ++++++++- .../controller/python/templates/helper.js | 65 ++++++- src-electron/util/zcl-util.js | 52 +++++ 15 files changed, 946 insertions(+), 86 deletions(-) diff --git a/src-electron/db/db-mapping.js b/src-electron/db/db-mapping.js index ec75213ba9..a533b77ba1 100644 --- a/src-electron/db/db-mapping.js +++ b/src-electron/db/db-mapping.js @@ -23,6 +23,7 @@ const dbApi = require('./db-api.js') const dbEnums = require('../../src-shared/db-enum.js') +const bin = require('../util/bin') exports.map = { package: (x) => { @@ -93,7 +94,7 @@ exports.map = { manufacturerCode: x.MANUFACTURER_CODE, name: x.NAME, label: x.NAME, - type: x.TYPE, + type: x.TYPE != 'array' ? x.TYPE : x.ARRAY_TYPE, side: x.SIDE, define: x.DEFINE, min: x.MIN, @@ -105,15 +106,20 @@ exports.map = { reportableChange: x.REPORTABLE_CHANGE, reportableChangeLength: x.REPORTABLE_CHANGE_LENGTH, isWritable: dbApi.fromDbBool(x.IS_WRITABLE), + isWritableAttribute: dbApi.fromDbBool(x.IS_WRITABLE), isNullable: dbApi.fromDbBool(x.IS_NULLABLE), defaultValue: x.DEFAULT_VALUE, isOptional: dbApi.fromDbBool(x.IS_OPTIONAL), isReportable: x.REPORTING_POLICY == dbEnums.reportingPolicy.mandatory || x.REPORTING_POLICY == dbEnums.reportingPolicy.suggested, + isReportableAttribute: + x.REPORTING_POLICY == dbEnums.reportingPolicy.mandatory || + x.REPORTING_POLICY == dbEnums.reportingPolicy.suggested, reportingPolicy: x.REPORTING_POLICY, isSceneRequired: dbApi.fromDbBool(x.IS_SCENE_REQUIRED), entryType: x.ARRAY_TYPE, + isArray: x.ARRAY_TYPE ? 1 : 0, mustUseTimedWrite: dbApi.fromDbBool(x.MUST_USE_TIMED_WRITE), } }, @@ -475,36 +481,61 @@ exports.map = { endpointTypeAttributeExtended: (x) => { if (x == null) return undefined return { - endpointTypeRef: x.ENDPOINT_TYPE_REF, - clusterRef: x.CLUSTER_REF, + arrayType: x.ARRAY_TYPE, attributeRef: x.ATTRIBUTE_REF, - included: dbApi.fromDbBool(x.INCLUDED), - storageOption: x.STORAGE_OPTION, - singleton: dbApi.fromDbBool(x.SINGLETON), bounded: dbApi.fromDbBool(x.BOUNDED), - defaultValue: x.DEFAULT_VALUE, - includedReportable: dbApi.fromDbBool(x.INCLUDED_REPORTABLE), - minInterval: x.MIN_INTERVAL, - maxInterval: x.MAX_INTERVAL, - reportableChange: x.REPORTABLE_CHANGE, - name: x.NAME, // Attribute Name - code: x.CODE, // Attribute Code - side: x.SIDE, // Attribute Side - define: x.DEFINE, // Attribute define - type: x.TYPE, // Attribute type - mfgCode: x.MANUFACTURER_CODE - ? x.MANUFACTURER_CODE - : x.CLUSTER_MANUFACTURER_CODE, // Attribute manufacturer code + clusterDefine: x.CLUSTER_DEFINE, clusterMfgCode: x.CLUSTER_MANUFACTURER_CODE, clusterName: x.CLUSTER_NAME, - clusterDefine: x.CLUSTER_DEFINE, - isSingleton: dbApi.fromDbBool(x.SINGLETON), // Endpoint type attribute is singleton or not + clusterRef: x.CLUSTER_REF, + clusterSide: x.SIDE, + code: x.CODE, // Attribute Code + defaultValue: x.DEFAULT_VALUE, + define: x.DEFINE, // Attribute define + endpointId: x.ENDPOINT_IDENTIFIER, // Endpoint type attribute's endpoint Id + endpointTypeRef: x.ENDPOINT_TYPE_REF, + entryType: x.ARRAY_TYPE, + hexCode: '0x' + bin.int16ToHex(x['CODE'] ? x['CODE'] : 0), // Attribute code in hex + id: x.ATTRIBUTE_ID, // Attribute id + included: dbApi.fromDbBool(x.INCLUDED), + includedReportable: dbApi.fromDbBool(x.INCLUDED_REPORTABLE), // Is attribute reportable + isArray: x.IS_ARRAY, // Is attribute of type array + isBound: dbApi.fromDbBool(x.BOUNDED), // Is endpoint type attribute bounded + isClusterEnabled: x.ENABLED, + isGlobalAttribute: x.IS_GLOBAL_ATTRIBUTE, // Is attribute global + isIncluded: dbApi.fromDbBool(x.INCLUDED), // Is endpoint type attribute included isManufacturingSpecific: dbApi.toDbBool( x.MANUFACTURER_CODE | x.CLUSTER_MANUFACTURER_CODE ), // Is Attribute mfg specific or not - endpointId: x.ENDPOINT_IDENTIFIER, // Endpoint type attribute's endpoint Id - tokenId: x.TOKEN_ID, // Endpoint type attribute's token id + isNullable: dbApi.fromDbBool(x.IS_NULLABLE), // Is attribute nullable + isOptionalAttribute: dbApi.fromDbBool(x.IS_OPTIONAL), + isReportableAttribute: dbApi.fromDbBool(x.INCLUDED_REPORTABLE), // Is attribute reportable + isSceneRequired: dbApi.fromDbBool(x.IS_SCENE_REQUIRED), + isSingleton: dbApi.fromDbBool(x.SINGLETON), // Endpoint type attribute is singleton or not + isWritable: dbApi.fromDbBool(x.IS_WRITABLE), // Is attribute writable + isWritableAttribute: dbApi.fromDbBool(x.IS_WRITABLE), // Is attribute writable + manufacturerCode: x.MANUFACTURER_CODE + ? x.MANUFACTURER_CODE + : x.CLUSTER_MANUFACTURER_CODE, // Attribute manufacturer code + max: x.MAX, // Attribute max value + maxInterval: x.MAX_INTERVAL, + maxLength: x.MAX_LENGTH, // Attribute max length + mfgCode: x.MANUFACTURER_CODE + ? x.MANUFACTURER_CODE + : x.CLUSTER_MANUFACTURER_CODE, // Attribute manufacturer code + min: x.MIN, // Attribute min value + minInterval: x.MIN_INTERVAL, + minLength: x.MIN_LENGTH, // Attribute min length + mustUseTimedWrite: dbApi.fromDbBool(x.MUST_USE_TIMED_WRITE), + name: x.NAME, // Attribute Name + reportableChange: x.REPORTABLE_CHANGE, + side: x.SIDE, // Attribute Side + singleton: dbApi.fromDbBool(x.SINGLETON), smallestEndpointIdentifier: x.SMALLEST_ENDPOINT_IDENTIFIER, // Smallest endpoint Id in which the attribute is present + storage: x.STORAGE_OPTION, + storageOption: x.STORAGE_OPTION, + tokenId: x.TOKEN_ID, // Endpoint type attribute's token id + type: x.TYPE != 'array' ? x.TYPE : x.ARRAY_TYPE, // Attribute type } }, diff --git a/src-electron/db/query-attribute.js b/src-electron/db/query-attribute.js index 41079dc0d7..2f5eb10b1d 100644 --- a/src-electron/db/query-attribute.js +++ b/src-electron/db/query-attribute.js @@ -23,21 +23,6 @@ const dbApi = require('./db-api.js') const dbMapping = require('./db-mapping.js') -function attributeExportMapping(x) { - return { - id: x.ATTRIBUTE_ID, - name: x.NAME, - code: x.CODE, - side: x.SIDE, - type: x.TYPE, - define: x.DEFINE, - mfgCode: x.MANUFACTURER_CODE, - clusterSide: x.SIDE, - clusterName: x.CLUSTER_NAME, - isClusterEnabled: x.ENABLED, - } -} - /** * Promises to select all endpoint type attributes filtered by EndpointTypeRef and ClusterRef. * @@ -146,13 +131,19 @@ async function duplicateEndpointTypeAttribute( * @param {*} db * @param {*} endpointTypeId * @param {*} packageIds + * @param {*} side * @returns Promise that resolves with the attribute data. */ async function selectAllAttributeDetailsFromEnabledClusters( db, endpointsAndClusters, - packageIds + packageIds, + side = null ) { + let sideFilter = '' + if (side) { + sideFilter = ` AND ATTRIBUTE.SIDE = '${side}' ` + } let endpointTypeClusterRef = endpointsAndClusters .map((ep) => ep.endpointTypeClusterRef) .toString() @@ -165,26 +156,71 @@ async function selectAllAttributeDetailsFromEnabledClusters( ATTRIBUTE.NAME, ATTRIBUTE.CODE, ATTRIBUTE.SIDE, - ATTRIBUTE.TYPE, + CASE + WHEN + ATTRIBUTE.ARRAY_TYPE IS NULL + THEN + ATTRIBUTE.TYPE + ELSE + ATTRIBUTE.ARRAY_TYPE + END AS TYPE, ATTRIBUTE.DEFINE, ATTRIBUTE.MANUFACTURER_CODE, ENDPOINT_TYPE_CLUSTER.SIDE, CLUSTER.NAME AS CLUSTER_NAME, - ENDPOINT_TYPE_CLUSTER.ENABLED + ENDPOINT_TYPE_CLUSTER.ENABLED, + CASE + WHEN + ATTRIBUTE.ARRAY_TYPE IS NOT NULL + THEN + 1 + ELSE + 0 + END AS IS_ARRAY, + ATTRIBUTE.IS_WRITABLE, + ATTRIBUTE.IS_NULLABLE, + ATTRIBUTE.MAX_LENGTH, + ATTRIBUTE.MIN_LENGTH, + ATTRIBUTE.MIN, + ATTRIBUTE.MAX, + ATTRIBUTE.ARRAY_TYPE, + ATTRIBUTE.MUST_USE_TIMED_WRITE, + ATTRIBUTE.IS_SCENE_REQUIRED, + ATTRIBUTE.IS_OPTIONAL, + CASE + WHEN + ATTRIBUTE.CLUSTER_REF IS NULL + THEN + 1 + ELSE + 0 + END AS IS_GLOBAL_ATTRIBUTE, + ENDPOINT_TYPE_ATTRIBUTE.INCLUDED_REPORTABLE, + ENDPOINT_TYPE_ATTRIBUTE.STORAGE_OPTION, + ENDPOINT_TYPE_ATTRIBUTE.SINGLETON, + ENDPOINT_TYPE_ATTRIBUTE.BOUNDED, + ENDPOINT_TYPE_ATTRIBUTE.INCLUDED, + ENDPOINT_TYPE_ATTRIBUTE.DEFAULT_VALUE, + ENDPOINT_TYPE_ATTRIBUTE.MIN_INTERVAL, + ENDPOINT_TYPE_ATTRIBUTE.MAX_INTERVAL, + ENDPOINT_TYPE_ATTRIBUTE.REPORTABLE_CHANGE FROM ATTRIBUTE INNER JOIN ENDPOINT_TYPE_ATTRIBUTE ON ATTRIBUTE.ATTRIBUTE_ID = ENDPOINT_TYPE_ATTRIBUTE.ATTRIBUTE_REF - INNER JOIN CLUSTER - ON ATTRIBUTE.CLUSTER_REF = CLUSTER.CLUSTER_ID INNER JOIN ENDPOINT_TYPE_CLUSTER - ON CLUSTER.CLUSTER_ID = ENDPOINT_TYPE_CLUSTER.CLUSTER_REF + ON ENDPOINT_TYPE_ATTRIBUTE.ENDPOINT_TYPE_CLUSTER_REF = ENDPOINT_TYPE_CLUSTER.ENDPOINT_TYPE_CLUSTER_ID + INNER JOIN CLUSTER + ON ENDPOINT_TYPE_CLUSTER.CLUSTER_REF = CLUSTER.CLUSTER_ID WHERE ENDPOINT_TYPE_CLUSTER.CLUSTER_REF IN (${endpointTypeClusterRef}) AND ENDPOINT_TYPE_ATTRIBUTE.INCLUDED = 1 - AND ATTRIBUTE.PACKAGE_REF IN (${dbApi.toInClause(packageIds)}) - GROUP BY ATTRIBUTE.NAME + AND ENDPOINT_TYPE_CLUSTER.ENABLED = 1 + AND ATTRIBUTE.PACKAGE_REF IN (${dbApi.toInClause(packageIds)}) + ${sideFilter} + GROUP BY CLUSTER.MANUFACTURER_CODE, CLUSTER.CODE, ATTRIBUTE.MANUFACTURER_CODE, ATTRIBUTE.CODE, ATTRIBUTE.SIDE + ORDER BY ATTRIBUTE.CODE ` ) - .then((rows) => rows.map(attributeExportMapping)) + .then((rows) => rows.map(dbMapping.map.endpointTypeAttributeExtended)) } /** @@ -247,7 +283,7 @@ async function selectAttributeDetailsFromAllEndpointTypesAndClustersUtil( GROUP BY ATTRIBUTE.NAME ` ) - .then((rows) => rows.map(attributeExportMapping)) + .then((rows) => rows.map(dbMapping.map.endpointTypeAttributeExtended)) } /** diff --git a/src-electron/db/query-bitmap.js b/src-electron/db/query-bitmap.js index c9220f4a86..306d151e14 100644 --- a/src-electron/db/query-bitmap.js +++ b/src-electron/db/query-bitmap.js @@ -68,6 +68,62 @@ WHERE (DATA_TYPE.NAME = ? OR DATA_TYPE.NAME = ?) AND DATA_TYPE.PACKAGE_REF IN ($ .then(dbMapping.map.bitmap) } +/** + * Select a bitmap matched by name and clusterId. + * @param {*} db + * @param {*} name + * @param {*} clusterId + * @param {*} packageIds + * @returns bitmap information or undefined + */ +async function selectBitmapByNameAndClusterId(db, name, clusterId, packageIds) { + let res = await dbApi + .dbAll( + db, + ` + SELECT + BITMAP.BITMAP_ID, + DATA_TYPE.NAME AS NAME, + BITMAP.SIZE AS SIZE + FROM BITMAP + INNER JOIN DATA_TYPE ON BITMAP.BITMAP_ID = DATA_TYPE.DATA_TYPE_ID + WHERE (DATA_TYPE.NAME = ? OR DATA_TYPE.NAME = ?) AND DATA_TYPE.PACKAGE_REF IN (${dbApi.toInClause( + packageIds + )})`, + [name, name.toLowerCase()] + ) + .then((rows) => rows.map(dbMapping.map.bitmap)) + + if (res && res.length == 1) { + return res[0] + } else { + return dbApi + .dbGet( + db, + ` +SELECT + BITMAP.BITMAP_ID, + DATA_TYPE.NAME AS NAME, + BITMAP.SIZE AS SIZE +FROM BITMAP +INNER JOIN + DATA_TYPE +ON + BITMAP.BITMAP_ID = DATA_TYPE.DATA_TYPE_ID +INNER JOIN + DATA_TYPE_CLUSTER +ON + DATA_TYPE_CLUSTER.DATA_TYPE_REF = BITMAP.BITMAP_ID +WHERE + (DATA_TYPE.NAME = ? OR DATA_TYPE.NAME = ?) + AND DATA_TYPE_CLUSTER.CLUSTER_REF = ? + AND DATA_TYPE.PACKAGE_REF IN (${dbApi.toInClause(packageIds)})`, + [name, name.toLowerCase(), clusterId] + ) + .then(dbMapping.map.bitmap) + } +} + async function selectBitmapById(db, id) { return dbApi .dbGet( @@ -88,3 +144,6 @@ WHERE BITMAP_ID = ?`, exports.selectBitmapById = selectBitmapById exports.selectAllBitmaps = selectAllBitmaps exports.selectBitmapByName = dbCache.cacheQuery(selectBitmapByName) +exports.selectBitmapByNameAndClusterId = dbCache.cacheQuery( + selectBitmapByNameAndClusterId +) diff --git a/src-electron/db/query-data-type.js b/src-electron/db/query-data-type.js index 926bc2384a..d5d5463874 100644 --- a/src-electron/db/query-data-type.js +++ b/src-electron/db/query-data-type.js @@ -94,6 +94,81 @@ async function selectDataTypeByName(db, name, packageIds) { .then(dbMapping.map.dataType) } +/** + * Gathers the data type information based on data type name and + * clusterId along with its actual type from disciminator table. + * @param db + * @param name + * @param clusterId + * @param packageIds + * @returns Data type information + */ +async function selectDataTypeByNameAndClusterId( + db, + name, + clusterId, + packageIds +) { + let smallCaseName = name.toLowerCase() + let res = await dbApi + .dbAll( + db, + ` + SELECT + DATA_TYPE.DATA_TYPE_ID, + DATA_TYPE.NAME AS NAME, + DATA_TYPE.DESCRIPTION, + DATA_TYPE.DISCRIMINATOR_REF, + DATA_TYPE.PACKAGE_REF, + DISCRIMINATOR.NAME AS DISCRIMINATOR_NAME + FROM + DATA_TYPE + INNER JOIN + DISCRIMINATOR + ON + DATA_TYPE.DISCRIMINATOR_REF = DISCRIMINATOR.DISCRIMINATOR_ID + WHERE + (DATA_TYPE.NAME = ? OR DATA_TYPE.NAME = ?) AND DATA_TYPE.PACKAGE_REF IN (${dbApi.toInClause( + packageIds + )})`, + [name, smallCaseName] + ) + .then((rows) => rows.map(dbMapping.map.dataType)) + + if (res && res.length == 1) { + return res[0] + } else { + return dbApi + .dbGet( + db, + ` + SELECT + DATA_TYPE.DATA_TYPE_ID, + DATA_TYPE.NAME AS NAME, + DATA_TYPE.DESCRIPTION, + DATA_TYPE.DISCRIMINATOR_REF, + DATA_TYPE.PACKAGE_REF, + DISCRIMINATOR.NAME AS DISCRIMINATOR_NAME + FROM + DATA_TYPE + INNER JOIN + DISCRIMINATOR + ON + DATA_TYPE.DISCRIMINATOR_REF = DISCRIMINATOR.DISCRIMINATOR_ID + INNER JOIN + DATA_TYPE_CLUSTER + ON + DATA_TYPE.DATA_TYPE_ID = DATA_TYPE_CLUSTER.DATA_TYPE_REF + WHERE + (DATA_TYPE.NAME = ? OR DATA_TYPE.NAME = ?) + AND DATA_TYPE_CLUSTER.CLUSTER_REF = ? + AND DATA_TYPE.PACKAGE_REF IN (${dbApi.toInClause(packageIds)})`, + [name, smallCaseName, clusterId] + ) + .then(dbMapping.map.dataType) + } +} + /** * Gathers All the data types * @param db @@ -186,3 +261,6 @@ exports.selectDataTypeById = selectDataTypeById exports.selectDataTypeByName = dbCache.cacheQuery(selectDataTypeByName) exports.selectAllDataTypes = selectAllDataTypes exports.selectSizeFromType = selectSizeFromType +exports.selectDataTypeByNameAndClusterId = dbCache.cacheQuery( + selectDataTypeByNameAndClusterId +) diff --git a/src-electron/db/query-enum.js b/src-electron/db/query-enum.js index c1304d34e6..cf9d243a3a 100644 --- a/src-electron/db/query-enum.js +++ b/src-electron/db/query-enum.js @@ -210,9 +210,77 @@ ORDER BY NAME`, .then(dbMapping.map.enum) } +/** + * Select an enum matched by name and clusterId. + * + * @param {*} db + * @param {*} name + * @param {*} clusterId + * @param {*} packageIds + * @returns enum information or undefined + */ +async function selectEnumByNameAndClusterId(db, name, clusterId, packageIds) { + let res = await dbApi + .dbAll( + db, + ` +SELECT + ENUM.ENUM_ID, + DATA_TYPE.NAME AS NAME, + ENUM.SIZE AS SIZE +FROM + ENUM +INNER JOIN + DATA_TYPE +ON + ENUM.ENUM_ID = DATA_TYPE.DATA_TYPE_ID +WHERE + (DATA_TYPE.NAME = ? OR DATA_TYPE.NAME = ?)AND PACKAGE_REF IN (${dbApi.toInClause( + packageIds + )}) +ORDER BY NAME`, + [name, name.toLowerCase()] + ) + .then((rows) => rows.map(dbMapping.map.enum)) + + if (res && res.length == 1) { + return res[0] + } else { + return dbApi + .dbGet( + db, + ` +SELECT + ENUM.ENUM_ID, + DATA_TYPE.NAME AS NAME, + ENUM.SIZE AS SIZE +FROM + ENUM +INNER JOIN + DATA_TYPE +ON + ENUM.ENUM_ID = DATA_TYPE.DATA_TYPE_ID +INNER JOIN + DATA_TYPE_CLUSTER +ON + DATA_TYPE_CLUSTER.DATA_TYPE_REF = ENUM.ENUM_ID +WHERE + (DATA_TYPE.NAME = ? OR DATA_TYPE.NAME = ?) + AND DATA_TYPE_CLUSTER.CLUSTER_REF = ? + AND PACKAGE_REF IN (${dbApi.toInClause(packageIds)}) +ORDER BY NAME`, + [name, name.toLowerCase(), clusterId] + ) + .then(dbMapping.map.enum) + } +} + // exports exports.selectAllEnums = selectAllEnums exports.selectEnumByName = dbCache.cacheQuery(selectEnumByName) +exports.selectEnumByNameAndClusterId = dbCache.cacheQuery( + selectEnumByNameAndClusterId +) exports.selectEnumById = selectEnumById exports.selectClusterEnums = selectClusterEnums exports.selectAllEnumItemsById = selectAllEnumItemsById diff --git a/src-electron/db/query-number.js b/src-electron/db/query-number.js index fa3a1a7898..ce78d4dfb1 100644 --- a/src-electron/db/query-number.js +++ b/src-electron/db/query-number.js @@ -48,6 +48,61 @@ async function selectNumberByName(db, packageIds, name) { .then(dbMapping.map.number) } +/** + * Select a number matched by name and clusterId + * + * @param db + * @param name + * @param packageIds + * @returns number information or undefined + */ +async function selectNumberByNameAndClusterId(db, name, clusterId, packageIds) { + let res = await dbApi + .dbAll( + db, + ` + SELECT + NUMBER.NUMBER_ID, + NUMBER.IS_SIGNED, + DATA_TYPE.NAME AS NAME, + NUMBER.SIZE AS SIZE + FROM NUMBER + INNER JOIN DATA_TYPE ON NUMBER.NUMBER_ID = DATA_TYPE.DATA_TYPE_ID + WHERE NAME = ? AND PACKAGE_REF IN (${dbApi.toInClause(packageIds)})`, + [name] + ) + .then((rows) => rows.map(dbMapping.map.number)) + + if (res && res.length == 1) { + return res[0] + } else { + return dbApi + .dbGet( + db, + ` + SELECT + NUMBER.NUMBER_ID, + NUMBER.IS_SIGNED, + DATA_TYPE.NAME AS NAME, + NUMBER.SIZE AS SIZE + FROM + NUMBER + INNER + JOIN DATA_TYPE ON NUMBER.NUMBER_ID = DATA_TYPE.DATA_TYPE_ID + INNER JOIN + DATA_TYPE_CLUSTER + ON + DATA_TYPE_CLUSTER.DATA_TYPE_REF = NUMBER.NUMBER_ID + WHERE + NAME = ? + AND DATA_TYPE_CLUSTER.CLUSTER_REF = ? + AND PACKAGE_REF IN (${dbApi.toInClause(packageIds)})`, + [name, clusterId] + ) + .then(dbMapping.map.number) + } +} + /** * Select an number matched by id. * @@ -99,5 +154,8 @@ async function selectAllNumbers(db, packageId) { } exports.selectNumberByName = dbCache.cacheQuery(selectNumberByName) +exports.selectNumberByNameAndClusterId = dbCache.cacheQuery( + selectNumberByNameAndClusterId +) exports.selectAllNumbers = selectAllNumbers exports.selectNumberById = selectNumberById diff --git a/src-electron/db/query-struct.js b/src-electron/db/query-struct.js index 6bb12b7c90..84276ba586 100644 --- a/src-electron/db/query-struct.js +++ b/src-electron/db/query-struct.js @@ -94,6 +94,69 @@ ORDER BY .then(dbMapping.map.struct) } +/** + * Select a struct matched by name and clusterId + * @param {*} db + * @param {*} name + * @param {*} clusterId + * @param {*} packageIds + * @returns struct information or undefined + */ +async function selectStructByNameAndClusterId(db, name, clusterId, packageIds) { + let res = await dbApi + .dbAll( + db, + ` +SELECT + STRUCT.STRUCT_ID, + STRUCT.IS_FABRIC_SCOPED, + DATA_TYPE.NAME, + DATA_TYPE.DISCRIMINATOR_REF +FROM + STRUCT +INNER JOIN + DATA_TYPE ON STRUCT.STRUCT_ID = DATA_TYPE.DATA_TYPE_ID +WHERE + NAME = ? + AND PACKAGE_REF IN (${dbApi.toInClause(packageIds)}) +ORDER BY + NAME`, + [name] + ) + .then((rows) => rows.map(dbMapping.map.struct)) + + if (res && res.length == 1) { + return res[0] + } else { + return dbApi + .dbGet( + db, + ` + SELECT + STRUCT.STRUCT_ID, + STRUCT.IS_FABRIC_SCOPED, + DATA_TYPE.NAME, + DATA_TYPE.DISCRIMINATOR_REF + FROM + STRUCT + INNER JOIN + DATA_TYPE ON STRUCT.STRUCT_ID = DATA_TYPE.DATA_TYPE_ID + INNER JOIN + DATA_TYPE_CLUSTER + ON + DATA_TYPE.DATA_TYPE_ID = DATA_TYPE_CLUSTER.DATA_TYPE_REF + WHERE + NAME = ? + AND PACKAGE_REF IN (${dbApi.toInClause(packageIds)}) + AND DATA_TYPE_CLUSTER.CLUSTER_REF = ? + ORDER BY + NAME`, + [name, clusterId] + ) + .then(dbMapping.map.struct) + } +} + /** * Get all structs which have a cluster associated with them. If a struct is * present in more than one cluster then it can be grouped by struct name to @@ -144,5 +207,8 @@ WHERE exports.selectStructById = selectStructById exports.selectAllStructs = selectAllStructs exports.selectStructByName = dbCache.cacheQuery(selectStructByName) +exports.selectStructByNameAndClusterId = dbCache.cacheQuery( + selectStructByNameAndClusterId +) exports.selectStructsWithClusterAssociation = selectStructsWithClusterAssociation diff --git a/src-electron/db/query-zcl.js b/src-electron/db/query-zcl.js index 577f4986b5..2328ebffca 100644 --- a/src-electron/db/query-zcl.js +++ b/src-electron/db/query-zcl.js @@ -1132,18 +1132,27 @@ exports.selectAllEnumItemsById = queryEnum.selectAllEnumItemsById exports.selectAllEnumItems = queryEnum.selectAllEnumItems exports.selectEnumById = queryEnum.selectEnumById exports.selectEnumByName = queryEnum.selectEnumByName +exports.selectEnumByNameAndClusterId = queryEnum.selectEnumByNameAndClusterId exports.selectStructById = queryStruct.selectStructById exports.selectStructByName = queryStruct.selectStructByName +exports.selectStructByNameAndClusterId = + queryStruct.selectStructByNameAndClusterId exports.selectBitmapById = queryBitmap.selectBitmapById exports.selectAllBitmaps = queryBitmap.selectAllBitmaps exports.selectBitmapByName = queryBitmap.selectBitmapByName +exports.selectBitmapByNameAndClusterId = + queryBitmap.selectBitmapByNameAndClusterId exports.selectDataTypeById = queryDataType.selectDataTypeById exports.selectDataTypeByName = queryDataType.selectDataTypeByName +exports.selectDataTypeByNameAndClusterId = + queryDataType.selectDataTypeByNameAndClusterId exports.selectNumberByName = queryNumber.selectNumberByName +exports.selectNumberByNameAndClusterId = + queryNumber.selectNumberByNameAndClusterId exports.selectAllDiscriminators = queryDiscriminator.selectAllDiscriminators exports.selectAllDataTypes = queryDataType.selectAllDataTypes diff --git a/src-electron/generator/helper-attribute.js b/src-electron/generator/helper-attribute.js index 39eac9ca05..23d139bd11 100644 --- a/src-electron/generator/helper-attribute.js +++ b/src-electron/generator/helper-attribute.js @@ -17,6 +17,7 @@ const queryAttribute = require('../db/query-attribute') const templateUtil = require('./template-util') +const queryZcl = require('../db/query-zcl') async function featureBits(options) { if ('featureBits' in this) { @@ -65,6 +66,95 @@ async function attributeDefault(options) { return templateUtil.templatePromise(this.global, p) } +/** + * If helper that checks if an attribute is not supported based on language + * being generated for. + * For eg: In java, an attribute callback is not supported when it is a struct. + * However it is supported if it is an array of structs. + * Available Options: + * - language: attribute callback support based on language + * @param {*} type + * @param {*} isArray + * @param {*} clusterId + * @param {*} options + * @returns Promise of content + */ +async function if_unsupported_attribute_callback( + type, + isArray, + clusterId, + options +) { + let hash = options.hash + let language = hash && hash.language ? hash.language : null + if (language == 'java') { + let struct = null + if (isArray) { + return options.inverse(this) + } else { + let packageIds = await templateUtil.ensureZclPackageIds(this) + struct = await queryZcl.selectStructByNameAndClusterId( + this.global.db, + type, + clusterId, + packageIds + ) + if (struct) { + return options.fn(this) + } else { + return options.inverse(this) + } + } + } else { + return options.inverse(this) + } +} + +/** + * If helper that checks if an attribute is basic or not based on language + * being generated for. + * For eg: In java, an attribute is not basic if it is either nullable, optional, + * array type or struct. + * Note: This helper should be used within an attribute block helper. + * Available Options: + * - language: Determine basic attribute based on language. + * example: + * {{#if_basic_attribute type}} + * type is not basic + * {{else}} + * type is basic + * {{/if_basic_attribute}} + * @param {*} type + * @param {*} clusterId + * @param {*} options + * @returns Promise of content + */ +async function if_basic_attribute(type, clusterId, options) { + let hash = options.hash + let language = hash && hash.language ? hash.language : null + if (language == 'java') { + let struct = null + if (this.isNullable || this.isOptional || this.isArray) { + return options.inverse(this) + } else { + let packageIds = await templateUtil.ensureZclPackageIds(this) + struct = await queryZcl.selectStructByNameAndClusterId( + this.global.db, + type, + clusterId, + packageIds + ) + if (struct) { + return options.inverse(this) + } else { + return options.fn(this) + } + } + } else { + return options.fn(this) + } +} + // WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! // // Note: these exports are public API. Templates that might have been created in the past and are @@ -73,3 +163,5 @@ async function attributeDefault(options) { exports.global_attribute_default = attributeDefault exports.feature_bits = featureBits +exports.if_unsupported_attribute_callback = if_unsupported_attribute_callback +exports.if_basic_attribute = if_basic_attribute diff --git a/src-electron/generator/helper-session.js b/src-electron/generator/helper-session.js index 9cc3a49bd4..559edc357b 100644 --- a/src-electron/generator/helper-session.js +++ b/src-electron/generator/helper-session.js @@ -294,7 +294,8 @@ async function all_user_cluster_attribute_util( await queryAttribute.selectAllAttributeDetailsFromEnabledClusters( currentContext.global.db, endpointsAndClusters, - packageIds + packageIds, + side ) } else if (isManufacturingSpecific) { endpointAttributes = @@ -311,6 +312,10 @@ async function all_user_cluster_attribute_util( packageIds ) } + if ('removeKeys' in options.hash) { + let keys = options.hash.removeKeys.split(',') + keys.forEach((k) => endpointAttributes.map((attr) => delete attr[k.trim()])) + } let availableAttributes = [] for (let i = 0; i < endpointAttributes.length; i++) { @@ -612,18 +617,22 @@ function all_user_cluster_commands_irrespective_of_manufaturing_specification( /** * Creates endpoint type cluster attribute iterator. This fetches all - * manufacturing and non-manufaturing specific attributes which have been enabled - * on added endpoints + * manufacturing-specific and standard attributes which have been enabled on + * added endpoints based on the name and side of the cluster. When side + * is not mentioned then client and server attributes are returned. + * Available Options: + * - removeKeys: Removes one or more keys from the map(for eg keys in db-mapping.js) + * for eg:(#enabled_attributes_for_cluster_and_side + * [cluster-name], [cluster-side], removeKeys='isOptional, isNullable') + * will remove 'isOptional' and 'isNullable' from the results * + * @param name + * @param side * @param options * @returns Promise of the resolved blocks iterating over manufacturing specific - * and non-manufacturing specific cluster attributes. + * and standard cluster attributes. */ -function all_user_cluster_attributes_irrespective_of_manufatucuring_specification( - name, - side, - options -) { +function enabled_attributes_for_cluster_and_side(name, side, options) { return all_user_cluster_attribute_util(name, side, options, this, false, true) } @@ -1526,7 +1535,15 @@ exports.all_user_cluster_manufacturer_specific_attributes = exports.all_user_cluster_non_manufacturer_specific_attributes = all_user_cluster_non_manufacturer_specific_attributes exports.all_user_cluster_attributes_irrespective_of_manufatucuring_specification = - all_user_cluster_attributes_irrespective_of_manufatucuring_specification + enabled_attributes_for_cluster_and_side +exports.all_user_cluster_attributes_irrespective_of_manufatucuring_specification = + dep(enabled_attributes_for_cluster_and_side, { + from: 'all_user_cluster_attributes_irrespective_of_manufatucuring_specification', + to: 'enabled_attributes_for_cluster_and_side', + }) +exports.enabled_attributes_for_cluster_and_side = + enabled_attributes_for_cluster_and_side + exports.all_user_cluster_attributes_for_generated_defaults = all_user_cluster_attributes_for_generated_defaults exports.all_user_cluster_generated_attributes = diff --git a/src-electron/generator/helper-zcl.js b/src-electron/generator/helper-zcl.js index 471a68b4cd..68a234bee7 100644 --- a/src-electron/generator/helper-zcl.js +++ b/src-electron/generator/helper-zcl.js @@ -730,33 +730,39 @@ function zcl_attributes_client(options) { * Iterator over the server attributes. If it is used at toplevel, if iterates over all the server attributes * in the database. If used within zcl_cluster context, it iterates over all the server attributes * that belong to that cluster. - * + * Available Options: + * - removeKeys: Removes one or more keys from the map(for eg keys in db-mapping.js) + * for eg: (#zcl_attributes_server removeKeys='isOptional, isNullable') will remove 'isOptional' + * from the results * @param {*} options * @returns Promise of attribute iteration. */ -function zcl_attributes_server(options) { +async function zcl_attributes_server(options) { // If used at the toplevel, 'this' is the toplevel context object. // when used at the cluster level, 'this' is a cluster - let promise = templateUtil - .ensureZclPackageIds(this) - .then((packageIds) => { - if ('id' in this) { - // We're functioning inside a nested context with an id, so we will only query for this cluster. - return queryZcl.selectAttributesByClusterIdAndSideIncludingGlobal( - this.global.db, - this.id, - packageIds, - dbEnum.side.server - ) - } else { - return queryZcl.selectAllAttributesBySide( - this.global.db, - dbEnum.side.server, - packageIds - ) - } - }) - .then((atts) => templateUtil.collectBlocks(atts, options, this)) + let packageIds = await templateUtil.ensureZclPackageIds(this) + let serverAttributes = '' + if ('id' in this) { + // We're functioning inside a nested context with an id, so we will only query for this cluster. + serverAttributes = + await queryZcl.selectAttributesByClusterIdAndSideIncludingGlobal( + this.global.db, + this.id, + packageIds, + dbEnum.side.server + ) + } else { + serverAttributes = await queryZcl.selectAllAttributesBySide( + this.global.db, + dbEnum.side.server, + packageIds + ) + } + if ('removeKeys' in options.hash) { + let keys = options.hash.removeKeys.split(',') + keys.forEach((k) => serverAttributes.map((attr) => delete attr[k.trim()])) + } + let promise = templateUtil.collectBlocks(serverAttributes, options, this) return templateUtil.templatePromise(this.global, promise) } @@ -2693,6 +2699,135 @@ function if_compare(leftValue, rightValue, options) { } } +/** + * Available Options: + * - asUpperCamelCase: 0/1 to return cpp data type starting with lower/upper + * camel case + * @param {*} type + * @param {*} dataType + * @param {*} hash + * @param {*} context + * @returns The corresponding chip callback name for a zcl data type. + */ +async function as_underlying_zcl_type_chip_callback_name( + type, + dataType, + hash, + context +) { + let res = await new Promise((resolve, reject) => { + let asUpperCamelCase = + hash && hash.asUpperCamelCase ? hash.asUpperCamelCase : false + if (dataType.discriminatorName.toLowerCase() == dbEnum.zclType.string) { + if (octetStringTypes.includes(type.toUpperCase())) { + resolve('OctetString') + } else if (characterStringTypes.includes(type.toUpperCase())) { + resolve('CharString') + } else if (context.isArray) { + resolve('List') + } else { + resolve(null) + } + } else if (dataType.name.includes('float')) { + return asUpperCamelCase ? resolve('Float') : resolve('float') + } else if (dataType.name.includes('double')) { + return asUpperCamelCase ? resolve('Double') : resolve('double') + } else if (dataType.name.includes('single')) { + return asUpperCamelCase ? resolve('Float') : resolve('float') + } + resolve(null) + }) + return res +} + +/* + * Available Options(Certain options are restricted based on modfier specified): + * - modifier: Specify the type of modifier. for eg: chipCallbackName + * - roundUpToPowerOfTwo: Rounds the size up to the nearest power of 2 + * - sizeIn: By default size is returned in bytes but it can be returned in bits + * by mentioning sizeIn="bits" + * - signedPrefix: Add a prefix string to the signed underlying zcl type size + * - unSignedPrefix: Add a prefix string to the unsigned underlying zcl type size + * - signedPostfix: Add a postfix string to the signed underlying zcl type size + * - unSignedPostfix: Add a postfix string to the unsigned underlying zcl type size + * - All other options passed to this helper are considered as overrides for + * zcl types + * for eg: (as_underlying_zcl_type_modifier type clusterId single="float") + * will return the zcl data type "float" for "single" + * Note: "single" is an exception here + * @param {*} type + * @param {*} clusterId + * @param {*} options + * @returns the data type based on modefier specified with customizations + * using options + * + */ +async function as_underlying_zcl_type_modifier(type, clusterId, options) { + let hash = options.hash + let signedPrefix = hash && hash.signedPrefix ? hash.signedPrefix : '' + let unSignedPrefix = hash && hash.unSignedPrefix ? hash.unSignedPrefix : '' + let unSignedPostfix = hash && hash.unSignedPostfix ? hash.unSignedPostfix : '' + let signedPostfix = hash && hash.signedPostfix ? hash.signedPostfix : '' + let sizeMultiple = 1 + let result = 0 + let isSigned = 0 + let modifier = hash && hash.modifier ? hash.modifier : null + if (hash && hash.sizeIn == 'bits') { + sizeMultiple = 8 + } + + // Get ZCL Data Type from the db + const packageIds = await templateUtil.ensureZclPackageIds(this) + let dataType = await queryZcl.selectDataTypeByNameAndClusterId( + this.global.db, + type, + clusterId, + packageIds + ) + if (!dataType) { + env.logWarning(type + ' not found in the data_type table') + return 0 + } + + if (dataType) { + // Overwrite any type with the one coming from the template options + // Eg: {{as_underlying_zcl_type_modifier type boolean='bool'}} + // Here all types named 'boolean' will return 'bool' + if (type in hash) { + return hash[type] + } + if (modifier && modifier == 'chipCallbackName') { + let callbackType = await as_underlying_zcl_type_chip_callback_name( + type, + dataType, + hash, + this + ) + if (callbackType) { + return callbackType + } + } + let sizeAndSign = await zclUtil.zcl_data_type_size_and_sign( + type, + dataType, + clusterId, + packageIds, + this + ) + result = sizeAndSign.size + isSigned = sizeAndSign.isSigned + } + result = + hash && hash.roundUpToPowerOfTwo + ? Math.pow(2, Math.ceil(Math.log2(result))) + : result + return ( + (isSigned ? signedPrefix : unSignedPrefix) + + result * sizeMultiple + + (isSigned ? signedPostfix : unSignedPostfix) + ) +} + const dep = templateUtil.deprecatedHelper // WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! @@ -2864,3 +2999,4 @@ exports.as_type_max_value = as_type_max_value exports.as_type_min_value = as_type_min_value exports.as_zcl_type_size = as_zcl_type_size exports.if_compare = if_compare +exports.as_underlying_zcl_type_modifier = as_underlying_zcl_type_modifier diff --git a/src-electron/generator/matter/app/zap-templates/templates/chip/helper.js b/src-electron/generator/matter/app/zap-templates/templates/chip/helper.js index 493d726b58..19f3db722c 100644 --- a/src-electron/generator/matter/app/zap-templates/templates/chip/helper.js +++ b/src-electron/generator/matter/app/zap-templates/templates/chip/helper.js @@ -745,6 +745,8 @@ async function chip_access_elements(options) { return templateUtil.templatePromise(this.global, p); } +const dep = templateUtil.deprecatedHelper; + // // Module exports // @@ -762,7 +764,12 @@ 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_attributes = dep( + chip_server_cluster_attributes, + 'chip_server_cluster_attributes has been deprecated. Use \ + enabled_attributes_for_cluster_and_side and \ + zcl_attributes_server to get enabled and all server attributes respectively' +); exports.chip_server_cluster_events = chip_server_cluster_events; exports.chip_server_has_list_attributes = chip_server_has_list_attributes; exports.chip_server_has_reportable_attributes = @@ -772,7 +779,9 @@ exports.chip_endpoints = chip_endpoints; exports.chip_endpoint_clusters = chip_endpoint_clusters; exports.if_chip_enum = if_chip_enum; exports.if_chip_complex = if_chip_complex; -exports.if_basic_global_response = if_basic_global_response; +exports.if_basic_global_response = dep(if_basic_global_response, { + to: 'if_basic_attribute', +}); exports.chip_cluster_specific_structs = chip_cluster_specific_structs; exports.chip_shared_structs = chip_shared_structs; exports.chip_access_elements = chip_access_elements; diff --git a/src-electron/generator/matter/controller/java/templates/helper.js b/src-electron/generator/matter/controller/java/templates/helper.js index f2db53d41f..5a692ecca1 100644 --- a/src-electron/generator/matter/controller/java/templates/helper.js +++ b/src-electron/generator/matter/controller/java/templates/helper.js @@ -23,6 +23,11 @@ const ChipTypesHelper = require('../../../app/zap-templates/common/ChipTypesHelp const StringHelper = require('../../../app/zap-templates/common/StringHelper.js'); const appHelper = require('../../../app/zap-templates/templates/app/helper.js'); const dbEnum = require('../../../../../../src-shared/db-enum'); +const queryZcl = require(zapPath + 'db/query-zcl'); +const zclUtil = require(zapPath + 'util/zcl-util.js'); + +const characterStringTypes = ['CHAR_STRING', 'LONG_CHAR_STRING']; +const octetStringTypes = ['OCTET_STRING', 'LONG_OCTET_STRING']; function convertBasicCTypeToJavaType(cType) { switch (cType) { @@ -187,6 +192,80 @@ function convertAttributeCallbackTypeToJavaName(cType) { } } +/** + * Available options: + * - isBoxedJavaType: 0/1 to return string types in different ways + * - All other options passed to this helper are considered as overrides for + * zcl types + * for eg: (as_underlying_java_zcl_type type clusterId boolean='Boolean') + * will return "Boolean" for "boolean" type + * @param {*} type + * @param {*} clusterId + * @param {*} options + * @returns The corresponding java data type for a zcl data type. + */ +async function as_underlying_java_zcl_type(type, clusterId, options) { + let hash = options.hash; + // Overwrite any type with the one coming from the template options + // Eg: {{as_underlying_java_zcl_type type [clusterId] boolean='Boolean'}} + // Here all types named 'boolean' will be returned as 'Boolean' + if (type in hash) { + return hash[type]; + } + + // Get ZCL Data Type from the db + const packageIds = await templateUtil.ensureZclPackageIds(this); + let dataType = await queryZcl.selectDataTypeByNameAndClusterId( + this.global.db, + type, + clusterId, + packageIds + ); + + if (!dataType) { + env.logWarning(type + ' not found in the data_type table'); + return 0; + } + let isBoxedJavaType = + hash && hash.isBoxedJavaType ? hash.isBoxedJavaType : false; + if ( + dataType.discriminatorName.toLowerCase() == dbEnum.zclType.bitmap || + dataType.discriminatorName.toLowerCase() == dbEnum.zclType.enum || + dataType.discriminatorName.toLowerCase() == dbEnum.zclType.number + ) { + if (dataType.name.includes('float')) { + return 'Float'; + } else if (dataType.name.includes('double')) { + return 'Double'; + } else if (dataType.name.includes('single')) { + return 'Float'; + } else { + let sizeAndSign = await zclUtil.zcl_data_type_size_and_sign( + type, + dataType, + clusterId, + packageIds, + this + ); + if (sizeAndSign.size >= 3) { + return 'Long'; + } + } + return 'Integer'; + } else if (octetStringTypes.includes(type.toUpperCase())) { + return isBoxedJavaType ? 'byte[]' : 'OctetString'; + } else if (characterStringTypes.includes(type.toUpperCase())) { + return isBoxedJavaType ? 'String' : 'CharString'; + } else { + let error = 'Unhandled type ' + type; + if (isBoxedJavaType) { + return 'Object'; + } else { + throw error; + } + } +} + async function asUnderlyingBasicType(type) { const options = { hash: {} }; let zclType = await zclHelper.asUnderlyingZclType.call(this, type, options); @@ -342,12 +421,16 @@ function incrementDepth(depth) { return depth + 1; } +const dep = templateUtil.deprecatedHelper; + // // Module exports // exports.asUnderlyingBasicType = asUnderlyingBasicType; exports.asJavaType = asJavaType; -exports.asJavaBoxedType = asJavaBoxedType; +exports.asJavaBoxedType = dep(asJavaBoxedType, { + to: 'as_underlying_java_zcl_type', +}); exports.asJniType = asJniType; exports.asJniSignature = asJniSignature; exports.asJniClassName = asJniClassName; @@ -356,11 +439,14 @@ exports.asJniSignatureBasic = asJniSignatureBasic; exports.convertBasicCTypeToJniType = convertBasicCTypeToJniType; exports.convertCTypeToJniSignature = convertCTypeToJniSignature; exports.convertBasicCTypeToJavaBoxedType = convertBasicCTypeToJavaBoxedType; -exports.convertAttributeCallbackTypeToJavaName = - convertAttributeCallbackTypeToJavaName; +exports.convertAttributeCallbackTypeToJavaName = dep( + convertAttributeCallbackTypeToJavaName, + { to: 'as_underlying_java_zcl_type' } +); exports.incrementDepth = incrementDepth; exports.meta = { category: dbEnum.helperCategory.matter, alias: ['controller/java/templates/helper.js', 'matter-java-helper'], }; +exports.as_underlying_java_zcl_type = as_underlying_java_zcl_type; diff --git a/src-electron/generator/matter/controller/python/templates/helper.js b/src-electron/generator/matter/controller/python/templates/helper.js index 5d0ddead17..9b2aadf25f 100644 --- a/src-electron/generator/matter/controller/python/templates/helper.js +++ b/src-electron/generator/matter/controller/python/templates/helper.js @@ -16,8 +16,14 @@ */ // Import helpers from zap core +const zapPath = '../../../../../'; const dbEnum = require('../../../../../../src-shared/db-enum'); const ChipTypesHelper = require('../../../app/zap-templates/common/ChipTypesHelper'); +const templateUtil = require(zapPath + 'generator/template-util.js'); +const queryZcl = require(zapPath + 'db/query-zcl'); + +const characterStringTypes = ['CHAR_STRING', 'LONG_CHAR_STRING']; +const octetStringTypes = ['OCTET_STRING', 'LONG_OCTET_STRING']; function asPythonType(zclType) { const type = ChipTypesHelper.asBasicType(zclType); @@ -43,6 +49,58 @@ function asPythonType(zclType) { } } +/** + * + * Available options: + * - All options passed to this helper are considered as overrides for + * zcl types + * for eg: (as_underlying_python_zcl_type type clusterId boolean='bool') + * will return "bool" for "boolean" type + * @param {*} type + * @param {*} clusterId + * @param {*} options + * @returns The corresponding python data type for a zcl data type. + */ +async function as_underlying_python_zcl_type(type, clusterId, options) { + let hash = options.hash; + // Overwrite any type with the one coming from the template options + // Eg: {{as_underlying_python_zcl_type type [clusterId] boolean='bool'}} + // Here all types named 'boolean' will be returned as 'bool' + if (type in hash) { + return hash[type]; + } + + // Get ZCL Data Type from the db + const packageIds = await templateUtil.ensureZclPackageIds(this); + let dataType = await queryZcl.selectDataTypeByNameAndClusterId( + this.global.db, + type, + clusterId, + packageIds + ); + if ( + dataType.discriminatorName.toLowerCase() == dbEnum.zclType.bitmap || + dataType.discriminatorName.toLowerCase() == dbEnum.zclType.enum || + dataType.discriminatorName.toLowerCase() == dbEnum.zclType.number + ) { + // returning nothing for floats + if ( + dataType.name.includes('float') || + dataType.name.includes('double') || + dataType.name.includes('single') + ) { + return ''; + } + return 'int'; + } else if (octetStringTypes.includes(type.toUpperCase())) { + return 'bytes'; + } else if (characterStringTypes.includes(type.toUpperCase())) { + return 'str'; + } else { + return ''; + } +} + function asPythonCType(zclType) { const type = ChipTypesHelper.asBasicType(zclType); switch (type) { @@ -62,13 +120,18 @@ function asPythonCType(zclType) { } } +const dep = templateUtil.deprecatedHelper; + // // Module exports // -exports.asPythonType = asPythonType; +exports.asPythonType = dep(asPythonType, { + to: 'as_underlying_python_zcl_type', +}); exports.asPythonCType = asPythonCType; exports.meta = { category: dbEnum.helperCategory.matter, alias: ['controller/python/templates/helper.js', 'matter-python-helper'], }; +exports.as_underlying_python_zcl_type = as_underlying_python_zcl_type; diff --git a/src-electron/util/zcl-util.js b/src-electron/util/zcl-util.js index 64b38c70f7..6330643f6a 100644 --- a/src-electron/util/zcl-util.js +++ b/src-electron/util/zcl-util.js @@ -797,6 +797,57 @@ async function createCommandSignature(db, packageId, cmd) { } } +/** + * + * @param {*} type + * @param {*} dataType + * @param {*} clusterId + * @param {*} packageIds + * @param {*} context + * @returns The size and sign of a zcl data type + */ +async function zcl_data_type_size_and_sign( + type, + dataType, + clusterId, + packageIds, + context +) { + let result = 0 + let isSigned = false + if (dataType.discriminatorName.toLowerCase() == dbEnum.zclType.bitmap) { + let bitmap = await queryZcl.selectBitmapByNameAndClusterId( + context.global.db, + dataType.name, + clusterId, + packageIds + ) + result = bitmap.size + } else if (dataType.discriminatorName.toLowerCase() == dbEnum.zclType.enum) { + let en = await queryZcl.selectEnumByNameAndClusterId( + context.global.db, + dataType.name, + clusterId, + packageIds + ) + result = en.size + } else if ( + dataType.discriminatorName.toLowerCase() == dbEnum.zclType.number + ) { + let number = await queryZcl.selectNumberByNameAndClusterId( + context.global.db, + dataType.name, + clusterId, + packageIds + ) + isSigned = number.isSigned + result = number.size + } else { + env.logWarning(type + ' is a complex type and size could not be determined') + } + return { size: result, isSigned: isSigned } +} + exports.clusterComparator = clusterComparator exports.attributeComparator = attributeComparator exports.commandComparator = commandComparator @@ -811,3 +862,4 @@ exports.determineType = determineType exports.dataTypeCharacterFormatter = dataTypeCharacterFormatter exports.calculateBytes = calculateBytes exports.createCommandSignature = createCommandSignature +exports.zcl_data_type_size_and_sign = zcl_data_type_size_and_sign