From 73d8e46a84922cadebf21dda5dc586db7ba62504 Mon Sep 17 00:00:00 2001 From: Timotej Ecimovic Date: Wed, 3 Feb 2021 10:21:09 -0400 Subject: [PATCH 1/9] Remove stray console log. --- test/tokens.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/tokens.test.js b/test/tokens.test.js index c10b0ee161..8498c40d63 100644 --- a/test/tokens.test.js +++ b/test/tokens.test.js @@ -266,7 +266,6 @@ test( 'DEFINE_BASIC_TOKEN(APPLICATION_VERSION_7, tokType_application_version, 11)' ) ).toBeTruthy() - console.log(header) expect( header.includes( "DEFINE_BASIC_TOKEN(PRODUCT_CODE_1, tokType_product_code, { 3, 'A', 'B', 'C', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, })" From 247939fa674e04080bd5e4f864e21f6f11d737e9 Mon Sep 17 00:00:00 2001 From: Timotej Ecimovic Date: Wed, 3 Feb 2021 10:29:43 -0400 Subject: [PATCH 2/9] Increase feature level. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b8b6c7e26d..f6fb20b318 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "zap", "version": "0.99.2", - "featureLevel": 9, + "featureLevel": 10, "description": "Configuration tool for the Zigbee Cluster Library", "productName": "zap", "cordovaId": "", From 6d5e503c3b876907383b7953daa68249db15a935 Mon Sep 17 00:00:00 2001 From: Timotej Ecimovic Date: Wed, 3 Feb 2021 10:57:44 -0400 Subject: [PATCH 3/9] Allow padding to be optional for the string types. --- src-electron/util/bin.js | 28 ++++++++++++++++++++++------ src-electron/util/types.js | 4 ++-- test/bin.test.js | 28 +++++++++++++++++++++++----- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src-electron/util/bin.js b/src-electron/util/bin.js index 321a5a8b6d..8f68acff0c 100644 --- a/src-electron/util/bin.js +++ b/src-electron/util/bin.js @@ -158,19 +158,27 @@ function hexToBinary(hex) { * * @param {*} value * @param {*} maxLength the maximum length of the used memory in bytes + * @param {*} pad If true, then pad with 0x00 until maxLength bytes. + * @returns Object containing 'length' and 'content', where length + * is number of bytes used and content is actual content in C format. */ -function stringToOneByteLengthPrefixCBytes(value, maxLength) { +function stringToOneByteLengthPrefixCBytes(value, maxLength, pad = true) { let len = value.length let ret = `${len}, ` for (let i = 0; i < len; i++) { ret = ret.concat(`'${value[i]}', `) } - if (maxLength > len + 1) { + let totalBytes = len + 1 + if (pad && maxLength > len + 1) { for (let i = 0; i < maxLength - (len + 1); i++) { ret = ret.concat('0x00, ') + totalBytes++ } } - return ret + return { + content: ret, + length: totalBytes, + } } /** @@ -180,20 +188,28 @@ function stringToOneByteLengthPrefixCBytes(value, maxLength) { * * @param {*} value * @param {*} maxLength the maximum length of the used memory in bytes + * @param {*} pad If true, then pad with 0x00 until maxLength bytes. + * @returns Object containing 'length' and 'content', where length + * is number of bytes used and content is actual content in C format. */ -function stringToTwoByteLengthPrefixCBytes(value, maxLength) { +function stringToTwoByteLengthPrefixCBytes(value, maxLength, pad = true) { let len = value.length let ret = `${(len >> 8) & 0xff}, ` ret = ret.concat(`${len & 0xff}, `) for (let i = 0; i < len; i++) { ret = ret.concat(`'${value[i]}', `) } - if (maxLength > len + 1) { + let totalBytes = len + 2 + if (pad && maxLength > len + 2) { for (let i = 0; i < maxLength - (len + 2); i++) { ret = ret.concat('0x00, ') + totalBytes++ } } - return ret + return { + content: ret, + length: totalBytes, + } } exports.int32ToHex = int32ToHex diff --git a/src-electron/util/types.js b/src-electron/util/types.js index c3f732610d..73122b88d6 100644 --- a/src-electron/util/types.js +++ b/src-electron/util/types.js @@ -78,9 +78,9 @@ function longTypeDefaultValue(size, type, value) { v = '0x00, '.repeat(size) } else if (isNaN(value)) { if (isOneBytePrefixedString(type)) { - v = bin.stringToOneByteLengthPrefixCBytes(value, size) + v = bin.stringToOneByteLengthPrefixCBytes(value, size).content } else if (isTwoBytePrefixedString(type)) { - v = bin.stringToTwoByteLengthPrefixCBytes(value, size) + v = bin.stringToTwoByteLengthPrefixCBytes(value, size).content } else { v = bin.hexToCBytes(bin.stringToHex(value)) } diff --git a/test/bin.test.js b/test/bin.test.js index fdfed68cab..03f7467e16 100644 --- a/test/bin.test.js +++ b/test/bin.test.js @@ -73,16 +73,34 @@ test('Bit offset', () => { }) test('ZCL strings', () => { - let r = bin.stringToOneByteLengthPrefixCBytes('Test string') - expect(r).toBe("11, 'T', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g', ") + let r + + r = bin.stringToOneByteLengthPrefixCBytes('Test string') + expect(r.content).toBe( + "11, 'T', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g', " + ) + r = bin.stringToTwoByteLengthPrefixCBytes('Test string') - expect(r).toContain( + expect(r.content).toContain( "0, 11, 'T', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g'," ) - r = bin.stringToTwoByteLengthPrefixCBytes('x'.repeat(300)) - expect(r).toContain( + expect(r.length).toBe(13) + + r = bin.stringToTwoByteLengthPrefixCBytes('x'.repeat(300), 400, false) + expect(r.content).toContain( "1, 44, 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'," ) + expect(r.length).toBe(302) + + r = bin.stringToOneByteLengthPrefixCBytes('Test', 10, true) + expect(r.content).toBe( + "4, 'T', 'e', 's', 't', 0x00, 0x00, 0x00, 0x00, 0x00, " + ) + expect(r.length).toBe(10) + + r = bin.stringToOneByteLengthPrefixCBytes('Test', 10, false) + expect(r.content).toBe("4, 'T', 'e', 's', 't', ") + expect(r.length).toBe(5) }) test('Bit count', () => { From 3f79e525d2b4dfc33b3ee9f8a38993c4b9da6f91 Mon Sep 17 00:00:00 2001 From: Jing Teng Date: Fri, 29 Jan 2021 10:52:05 -0500 Subject: [PATCH 4/9] run version check for packaged binaries. 1) "zap --version" display version information for all distros. for windows / linux, the binary inside the adapter pack .zap files were used. for mac, the binary inside /dist/mac were used due to jenkins unzip/zip not able to maintain framework symlinks properly and causes zap unable to be run properly. 2) backing down electron/electron-builder versions due to crashes Prior to reverting, the following error would be thrown via "zap --version" FATAL ERROR: Error::Error napi_create_reference 1: 0x10d3cacd5 node::Buffer::New(v8::Isolate*, char*, unsigned long, void (*)(char*, void*), void*) [/Users/buildengineer/mac-jenkins-slave-tools/jenkins-slave/workspace/version_check_for_electron_crash/dist/mac/zap.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework] 2: 0x10d3cad44 node::Buffer::New(v8::Isolate*, char*, unsigned long, void (*)(char*, void*), void*) [/Users/buildengineer/mac-jenkins-slave-tools/jenkins-slave/workspace/version_check_for_electron_crash/dist/mac/zap.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework] 3: 0x10d3cacf9 node::Buffer::New(v8::Isolate*, char*, unsigned long, void (*)(char*, void*), void*) [/Users/buildengineer/mac-jenkins-slave-tools/jenkins-slave/workspace/version_check_for_electron_crash/dist/mac/zap.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework] 4: 0x10d3a58e8 napi_fatal_error [/Users/buildengineer/mac-jenkins-slave-tools/jenkins-slave/workspace/version_check_for_electron_crash/dist/mac/zap.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework] 5: 0x112d3cc3d Napi::Error::Fatal(char const*, char const*) [/var/folders/v5/0p331z1s1qvfcmv0vjm3kn6w0000gn/T/.zap.id.2I0QU4] 6: 0x112d4261f Napi::Error::New(napi_env__*) [/var/folders/v5/0p331z1s1qvfcmv0vjm3kn6w0000gn/T/.zap.id.2I0QU4] 7: 0x112d42cb2 Napi::Function::MakeCallback(napi_value__*, unsigned long, napi_value__* const*, napi_async_context__*) const [/var/folders/v5/0p331z1s1qvfcmv0vjm3kn6w0000gn/T/.zap.id.2I0QU4] 8: 0x112d54dfd node_sqlite3::Statement::Work_AfterPrepare(napi_env__*, napi_status, void*) [/var/folders/v5/0p331z1s1qvfcmv0vjm3kn6w0000gn/T/.zap.id.2I0QU4] --- Jenkinsfile | 150 ++++++++++++++++++++++++++++---------- package-lock.json | 6 +- package.json | 4 +- src-electron/util/args.js | 1 + 4 files changed, 119 insertions(+), 42 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 13471fb65d..495c5c7aeb 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -113,7 +113,7 @@ pipeline script { gitBranch = "${env.BRANCH_NAME}" - sh '/home/buildengineer/tools/sonar-scanner/bin/sonar-scanner -Dsonar.host.url=https://sonarqube.silabs.net/ -Dsonar.login=e48b8a949e2869afa974414c56b4dc7baeb146e3 -X -Dsonar.branch.name='+gitBranch + sh '/home/buildengineer/tools/sonar-scanner/bin/sonar-scanner -Dsonar.host.url=https://sonarqube.silabs.net/ -Dsonar.login=e48b8a949e2869afa974414c56b4dc7baeb146e3 -X -Dsonar.branch.name=' + gitBranch } } } @@ -161,6 +161,33 @@ pipeline } stage('Building distribution artifacts') { parallel { + stage('Building for Mac') + { + agent { label 'bgbuild-mac' } + steps + { + script + { + withEnv(['PATH+LOCAL_BIN=/usr/local/bin']) + { + withCredentials([usernamePassword(credentialsId: 'buildengineer', + usernameVariable: 'SL_USERNAME', + passwordVariable: 'SL_PASSWORD')]) + { + sh 'npm list || true' + sh 'npm ci' + sh 'npm rebuild canvas --update-binary || true' + sh "security unlock-keychain -p ${SL_PASSWORD} login" + sh 'npm run version-stamp' + sh 'npm run build-spa' + sh 'npm run dist-mac' + sh 'npm run apack:mac' + stash includes: 'dist/zap_apack_mac.zip', name: 'zap_apack_mac' + } + } + } + } + } stage('Building for Windows / Linux') { steps @@ -170,70 +197,121 @@ pipeline sh 'echo "Building for Windows"' sh 'npm run dist-win' sh 'npm run apack:win' + stash includes: 'dist/zap_apack_win.zip', name: 'zap_apack_win' sh 'echo "Building for Linux"' sh 'npm run dist-linux' sh 'npm run apack:linux' + stash includes: 'dist/zap_apack_linux.zip', name: 'zap_apack_linux' } } } - stage('Building for Mac') + } + } + + stage('Artifact creation') + { + parallel { + stage('Creating artifact for Windows / Linux') { - agent { - label "bgbuild-mac" + steps + { + script + { + archiveArtifacts artifacts:'dist/zap*', fingerprint: true + } } + } + + stage('Creating artifact for Mac') + { + agent { label 'bgbuild-mac' } steps { script { - withEnv(['PATH+LOCAL_BIN=/usr/local/bin']) - { - withCredentials([usernamePassword(credentialsId: 'buildengineer', - usernameVariable: 'SL_USERNAME', - passwordVariable: 'SL_PASSWORD')]) - { - sh 'npm list || true' - sh 'npm ci' - sh 'npm rebuild canvas --update-binary || true' - sh "security unlock-keychain -p ${SL_PASSWORD} login" - sh 'npm run build-spa' - sh 'npm run dist-mac' - sh 'npm run apack:mac' - } - } + archiveArtifacts artifacts:'dist/zap*', fingerprint: true } } } } } - stage('Artifact creation') - { + + stage('Check version stamp inside binaries') { parallel { - stage('Creating artifact for Mac') + stage('Check version stamp for Windows') { - agent { - label "bgbuild-mac" + agent { label 'bgbuild-win' } + steps + { + dir('test_apack_bin') { + script + { + unstash 'zap_apack_win' + unzip zipFile: 'dist/zap_apack_win.zip' + String response = sh(script: "zap.exe --version", returnStdout: true).trim() + echo response + if ( response.indexOf("undefined") == -1) { + currentBuild.result = 'SUCCESS' + } else { + error "Undefined version information" + currentBuild.result = 'FAILURE' + } + } + } } + } + stage('Check version stamp for Mac') + { + agent { label 'bgbuild-mac' } steps { + // WORKAROUND: + // Skip testing zap within .zap since zip/unzip within Jenkins is unable to + // maintain the framework symlinks, referenced by ZAP script { - archiveArtifacts artifacts:'dist/zap*', fingerprint: true + // redirect stderr to file to avoid return statusCode failing the pipeline. + // mac binaries would err with "Trace/BPT trap: 5". + // depending on electron/electron-builder version, the error appear/disappear from time to time. + String status = sh(script: "./dist/mac/zap.app/Contents/MacOS/zap --version 2&> output.txt", returnStatus: true) + def output = readFile('output.txt').trim() + echo output + if ( output.indexOf("undefined") == -1) { + currentBuild.result = 'SUCCESS' + } else { + error "Undefined version information" + currentBuild.result = 'FAILURE' + } } } } - stage('Creating artifact for Windows / Linux') + + stage('Check version stamp for Linux') { steps { - script - { - archiveArtifacts artifacts:'dist/zap*', fingerprint: true + dir('test_apack_bin') { + script + { + unstash 'zap_apack_linux' + unzip zipFile: 'dist/zap_apack_linux.zip' + sh 'chmod 755 zap' + String response = sh(script: "./zap --version", returnStdout: true).trim() + echo response + if ( response.indexOf("undefined") == -1) { + currentBuild.result = 'SUCCESS' + } else { + error "Undefined version information" + currentBuild.result = 'FAILURE' + } + } } } } } } + stage('Build status resolution') { steps @@ -254,18 +332,16 @@ pipeline { script { - triggerRemoteJob blockBuildUntilComplete: false, - job: 'https://jnkaus016.silabs.com/job/Adapter_Pack_ZAP_64/', - remoteJenkinsName: 'jnkaus016', - shouldNotFailBuild: true, - useCrumbCache: true, + triggerRemoteJob blockBuildUntilComplete: false, + job: 'https://jnkaus016.silabs.com/job/Adapter_Pack_ZAP_64/', + remoteJenkinsName: 'jnkaus016', + shouldNotFailBuild: true, + useCrumbCache: true, useJobInfoCache: true - } } } - - } + } post { always { script diff --git a/package-lock.json b/package-lock.json index acbad4d3eb..79ba00b834 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10176,9 +10176,9 @@ "dev": true }, "electron": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-9.4.0.tgz", - "integrity": "sha512-hOC4q0jkb+UDYZRy8vrZ1IANnq+jznZnbkD62OEo06nU+hIbp2IrwDRBNuSLmQ3cwZMVir0WSIA1qEVK0PkzGA==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-9.3.1.tgz", + "integrity": "sha512-DScrhqBT4a54KfdF0EoipALpHmdQTn3m7SSCtbpTcEcG+UDUiXad2cOfW6DHeVH7N+CVDKDG12q2PhVJjXkFAA==", "dev": true, "requires": { "@electron/get": "^1.0.1", diff --git a/package.json b/package.json index f6fb20b318..2879f24168 100644 --- a/package.json +++ b/package.json @@ -95,8 +95,8 @@ "@vue/eslint-config-standard": "^6.0.0", "babel-eslint": "^10.1.0", "devtron": "^1.4.0", - "electron": "9.4.0", - "electron-builder": "^22.9.1", + "electron": "9.3.1", + "electron-builder": "^22.8.0", "electron-debug": "^3.2.0", "electron-devtools-installer": "^3.1.1", "electron-installer-dmg": "^3.0.0", diff --git a/src-electron/util/args.js b/src-electron/util/args.js index 8d8471b9e5..cc4e00f1be 100644 --- a/src-electron/util/args.js +++ b/src-electron/util/args.js @@ -168,6 +168,7 @@ For more information, see https://github.com/project-chip/zap` // Collect files that are passed as loose arguments let allFiles = ret._.filter((arg, index) => { if (index == 0) return false + if (typeof arg == 'number') return false if (arg.endsWith('.js')) return false if (arg in commands) return false return true From 8eda869e4374b59b4592e11f4016654f6523e6d3 Mon Sep 17 00:00:00 2001 From: Timotej Ecimovic Date: Fri, 5 Feb 2021 09:57:37 -0400 Subject: [PATCH 5/9] Add separate convert command from CLI. --- src-electron/main-process/startup.js | 22 ++++++++++++++++++++++ src-electron/util/args.js | 4 +++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src-electron/main-process/startup.js b/src-electron/main-process/startup.js index 5ca1282506..2ddcaa3819 100644 --- a/src-electron/main-process/startup.js +++ b/src-electron/main-process/startup.js @@ -103,6 +103,23 @@ function startNormal(uiEnabled, showUrl, zapFiles, options) { }) } +/** + * Perform file conversion. + * + * @param {*} files + * @param {*} output + */ +function startConvert(files, output, options = { log: true, quit: true }) { + if (options.log) console.log(`🤖 Conversion started`) + if (options.log) console.log(` 👉 input files: ${files}`) + if (options.log) console.log(` 👉 output file: ${output}`) + + if (options.log) console.log('😎 Conversion done!') + if (options.quit && app != null) { + app.quit() + } +} + /** * Perform file analysis. * @@ -323,6 +340,11 @@ function startUp(isElectron) { if (argv.zapFiles.length < 1) throw 'You need to specify at least one zap file.' return startAnalyze(argv.zapFiles) + } else if (argv._.includes('convert')) { + if (argv.zapFiles.length < 1) + throw 'You need to specify at least one zap file.' + if (argv.output == null) throw 'You need to specify output file.' + return startConvert(argv.zapFiles, argv.output) } else if (argv._.includes('generate')) { return startGeneration( argv.output, diff --git a/src-electron/util/args.js b/src-electron/util/args.js index cc4e00f1be..06f96d74f4 100644 --- a/src-electron/util/args.js +++ b/src-electron/util/args.js @@ -60,6 +60,7 @@ function processCommandLineArguments(argv) { generate: 'Generate ZCL artifacts.', selfCheck: 'Perform the self-check of the application.', analyze: 'Analyze the zap file without doing anything.', + convert: 'Convert a zap or ISC file to latest zap file.', } let y = yargs for (const cmd in commands) { @@ -126,7 +127,8 @@ function processCommandLineArguments(argv) { desc: 'Print out the URL that an external browser should use.', }) .option('output', { - desc: 'Specifying the output directory for generation', + desc: + 'Specifying the output directory for generation or output file for conversion.', alias: ['out', 'o'], type: 'string', }) From 9ac6d25bf8e4f6b2c41c3632d430a4eab645d0d8 Mon Sep 17 00:00:00 2001 From: Timotej Ecimovic Date: Fri, 5 Feb 2021 10:16:04 -0400 Subject: [PATCH 6/9] Implement the 'convert' flow, where `zap convert file.isc -o file.zap` performs the headless conversion. People don't have to run UI, load the file and then save the file any more. --- src-electron/main-process/startup.js | 33 ++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src-electron/main-process/startup.js b/src-electron/main-process/startup.js index 2ddcaa3819..1785f68998 100644 --- a/src-electron/main-process/startup.js +++ b/src-electron/main-process/startup.js @@ -29,6 +29,7 @@ const generatorEngine = require('../generator/generation-engine.js') const querySession = require('../db/query-session.js') const util = require('../util/util.js') const importJs = require('../importexport/import.js') +const exportJs = require('../importexport/export.js') const uiJs = require('./ui.js') // This file contains various startup modes. @@ -114,10 +115,34 @@ function startConvert(files, output, options = { log: true, quit: true }) { if (options.log) console.log(` 👉 input files: ${files}`) if (options.log) console.log(` 👉 output file: ${output}`) - if (options.log) console.log('😎 Conversion done!') - if (options.quit && app != null) { - app.quit() - } + let dbFile = env.sqliteFile('convert') + + return dbApi + .initDatabaseAndLoadSchema(dbFile, env.schemaFile(), env.zapVersion()) + .then((d) => { + db = d + if (options.log) console.log(' 👉 database and schema initialized') + return zclLoader.loadZcl(db, args.zclPropertiesFile) + }) + .then((d) => { + return util.executePromisesSequentially(files, (singlePath) => + importJs + .importDataFromFile(db, singlePath) + .then((sessionId) => { + if (options.log) console.log(' 👉 import done') + return exportJs.exportDataIntoFile(db, sessionId, output) + }) + .then(() => { + if (options.log) console.log(' 👉 export done') + }) + ) + }) + .then(() => { + if (options.log) console.log('😎 Conversion done!') + if (options.quit && app != null) { + app.quit() + } + }) } /** From 52a8c42401bf32ea745c161b8459f10f871e293c Mon Sep 17 00:00:00 2001 From: Timotej Ecimovic Date: Fri, 5 Feb 2021 11:01:12 -0400 Subject: [PATCH 7/9] Remove a DB reference ID from the export. --- src-electron/importexport/export.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src-electron/importexport/export.js b/src-electron/importexport/export.js index 8cf3919505..98a28934ce 100644 --- a/src-electron/importexport/export.js +++ b/src-electron/importexport/export.js @@ -87,11 +87,11 @@ async function exportEndpointTypes(db, sessionId) { return Promise.all(promises) .then(() => queryImpExp.exportEndpoints(db, sessionId, endpointTypes)) .then((endpoints) => { - return Promise.resolve(endpoints) - }) - .then((endpoints) => { - endpointTypes.forEach((ep) => { - delete ep.endpointTypeId + endpointTypes.forEach((ept) => { + delete ept.endpointTypeId + }) + endpoints.forEach((ep) => { + delete ep.endpointTypeRef }) return { endpointTypes: endpointTypes, endpoints: endpoints } }) From bbec04cc5d11df7cb37995548fb65b40b2cca81e Mon Sep 17 00:00:00 2001 From: Timotej Ecimovic Date: Fri, 5 Feb 2021 13:59:30 -0400 Subject: [PATCH 8/9] Add support for custom ZCL device. --- src-electron/db/query-zcl.js | 2 +- src-electron/zcl/zcl-loader-silabs.js | 28 ++++++++++++++++++++++++++- test/zcl-loader-consecutive.test.js | 2 +- test/zcl-loader.test.js | 2 +- zcl-builtin/silabs/zcl.json | 1 + 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src-electron/db/query-zcl.js b/src-electron/db/query-zcl.js index 3dcadfc67a..18eedd789b 100644 --- a/src-electron/db/query-zcl.js +++ b/src-electron/db/query-zcl.js @@ -1395,7 +1395,7 @@ INSERT INTO ATTRIBUTE ( * @export * @param {*} db * @param {*} packageId - * @param {*} data an array of objects that must contain: code, name, description + * @param {*} data an array of objects that must contain: domain, code, profileId, name, description * @returns Promise of an insertion of device types. */ async function insertDeviceTypes(db, packageId, data) { diff --git a/src-electron/zcl/zcl-loader-silabs.js b/src-electron/zcl/zcl-loader-silabs.js index 00c98863b2..0283f51aa3 100644 --- a/src-electron/zcl/zcl-loader-silabs.js +++ b/src-electron/zcl/zcl-loader-silabs.js @@ -79,6 +79,8 @@ async function collectDataFromJsonFile(ctx) { } ctx.version = obj.version + ctx.supportCustomZclDevice = obj.supportCustomZclDevice + env.logInfo(`Resolving: ${ctx.zclFiles}, version: ${ctx.version}`) resolve(ctx) }) @@ -142,7 +144,7 @@ async function collectDataFromPropertiesFile(ctx) { if (zclProps.defaults) { ctx.defaults = zclProps.defaults } - + ctx.supportCustomZclDevice = zclProps.supportCustomZclDevice ctx.version = zclProps.version env.logInfo(`Resolving: ${ctx.zclFiles}, version: ${ctx.version}`) resolve(ctx) @@ -1031,6 +1033,29 @@ async function loadIndividualSilabsFile(db, filePath, boundValidator) { }) } +/** + * If custom device is supported, then this method creates it. + * + * @param {*} db + * @param {*} ctx + * @returns context + */ +async function processCustomZclDeviceType(db, ctx) { + if (ctx.supportCustomZclDevice) { + let customDeviceTypes = [] + customDeviceTypes.push({ + domain: 'Custom', + code: 0xffff, + profileId: 0xffff, + name: 'Custom ZCL Device Type', + description: + 'Custom ZCL device type supports any combination of clusters.', + }) + await queryZcl.insertDeviceTypes(db, ctx.packageId, customDeviceTypes) + } + return ctx +} + /** * Toplevel function that loads the toplevel metafile * and orchestrates the promise chain. @@ -1056,6 +1081,7 @@ async function loadSilabsZcl(db, context, isJson = false) { .then((ctx) => zclLoader.recordVersion(ctx)) .then((ctx) => parseZclFiles(db, ctx)) .then((ctx) => parseManufacturerData(db, ctx)) + .then((ctx) => processCustomZclDeviceType(db, ctx)) .then((ctx) => parseOptions(db, ctx)) .then((ctx) => parseDefaults(db, ctx)) .then((ctx) => parseZclSchema(db, ctx)) diff --git a/test/zcl-loader-consecutive.test.js b/test/zcl-loader-consecutive.test.js index 96eecd61d0..5bf3f3231b 100644 --- a/test/zcl-loader-consecutive.test.js +++ b/test/zcl-loader-consecutive.test.js @@ -105,7 +105,7 @@ test('test that consecutive loading of metafiles properly avoids duplication', ( .then((x) => expect(x.length).toEqual(54)) .then(() => queryZcl.selectAllBitmaps(db, jsonPackageId)) .then(() => queryZcl.selectAllDeviceTypes(db, jsonPackageId)) - .then((x) => expect(x.length).toEqual(174)) + .then((x) => expect(x.length).toEqual(176)) .then(() => queryZcl.selectAllAtomics(db, jsonPackageId)) .then((x) => expect(x.length).toEqual(56)) .then(() => queryZcl.selectAllClusters(db, dotdotPackageId)) diff --git a/test/zcl-loader.test.js b/test/zcl-loader.test.js index 1cd305cacd..256f82dc4a 100644 --- a/test/zcl-loader.test.js +++ b/test/zcl-loader.test.js @@ -70,7 +70,7 @@ test('test Silabs zcl data loading in memory', () => { .then(() => queryZcl.selectAllBitmaps(db, packageId)) .then((x) => expect(x.length).toEqual(121)) .then(() => queryZcl.selectAllDeviceTypes(db, packageId)) - .then((x) => expect(x.length).toEqual(174)) + .then((x) => expect(x.length).toEqual(175)) .then(() => queryGeneric.selectCountFrom(db, 'COMMAND_ARG')) .then((x) => expect(x).toEqual(testUtil.totalCommandArgsCount)) .then(() => queryGeneric.selectCountFrom(db, 'COMMAND')) diff --git a/zcl-builtin/silabs/zcl.json b/zcl-builtin/silabs/zcl.json index 97bb1bbd8a..74e27f9d6e 100644 --- a/zcl-builtin/silabs/zcl.json +++ b/zcl-builtin/silabs/zcl.json @@ -26,6 +26,7 @@ "wwah-silabs-devices.xml" ], "zclSchema": "./schema/zcl.xsd", + "supportCustomZclDevice": true, "manufacturersXml": "../shared/manufacturers.xml", "zclValidation": "./schema/zcl-validation.js", "options": { From 79bc5b2a5539d671cdc37fdcdb35e69651dfb638 Mon Sep 17 00:00:00 2001 From: Bharat Dandu Date: Fri, 5 Feb 2021 11:56:26 -0500 Subject: [PATCH 9/9] Helpers for zcl cli - zap-cli.c - Added helpers for zcl cli to extract the commands with cli in zap databse - Bumping the feature level pointer - JIRA: ZAPP-459 --- package.json | 2 +- src-electron/db/query-zcl.js | 92 ++++++++++++++++++++++++ src-electron/generator/helper-session.js | 55 +++++++++++++- 3 files changed, 146 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 2879f24168..27f2cf141a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "zap", "version": "0.99.2", - "featureLevel": 10, + "featureLevel": 11, "description": "Configuration tool for the Zigbee Cluster Library", "productName": "zap", "cordovaId": "", diff --git a/src-electron/db/query-zcl.js b/src-electron/db/query-zcl.js index 18eedd789b..9b4e9a937e 100644 --- a/src-electron/db/query-zcl.js +++ b/src-electron/db/query-zcl.js @@ -2091,6 +2091,60 @@ async function exportAllCommandDetailsFromEnabledClusters( .then((rows) => rows.map(mapFunction)) } +/** + * + * @param db + * @param endpointsAndClusters + * @returns Returns a promise of data for commands with cli inside an endpoint type. + */ +async function exportAllCliCommandDetailsFromEnabledClusters( + db, + endpointsAndClusters +) { + let endpointTypeClusterRef = endpointsAndClusters + .map((ep) => ep.endpointTypeClusterRef) + .toString() + let mapFunction = (x) => { + return { + id: x.COMMAND_ID, + name: x.NAME, + code: x.CODE, + commandSource: x.SOURCE, + mfgCode: x.MANUFACTURER_CODE, + description: x.DESCRIPTION, + clusterSide: x.SIDE, + clusterName: x.CLUSTER_NAME, + isClusterEnabled: x.ENABLED, + } + } + return dbApi + .dbAll( + db, + ` + SELECT + COMMAND.COMMAND_ID, + COMMAND.NAME, + COMMAND.CODE, + COMMAND.SOURCE, + COMMAND.MANUFACTURER_CODE, + COMMAND.DESCRIPTION, + ENDPOINT_TYPE_CLUSTER.SIDE, + CLUSTER.NAME AS CLUSTER_NAME, + ENDPOINT_TYPE_CLUSTER.ENABLED + FROM COMMAND + INNER JOIN CLUSTER + ON COMMAND.CLUSTER_REF = CLUSTER.CLUSTER_ID + INNER JOIN ENDPOINT_TYPE_CLUSTER + ON CLUSTER.CLUSTER_ID = ENDPOINT_TYPE_CLUSTER.CLUSTER_REF + INNER JOIN PACKAGE_OPTION + ON PACKAGE_OPTION.OPTION_CODE = COMMAND.NAME + WHERE ENDPOINT_TYPE_CLUSTER.CLUSTER_REF in (${endpointTypeClusterRef}) + GROUP BY COMMAND.NAME, CLUSTER.NAME + ` + ) + .then((rows) => rows.map(mapFunction)) +} + /** * Get the number of command arguments for a command * @@ -2346,6 +2400,42 @@ async function exportCommandDetailsFromAllEndpointTypeCluster( .then((rows) => rows.map(mapFunction)) } +/** + * + * @param db + * @param endpointClusterId + * Returns: A promise with all commands with cli for a given cluster id + */ +async function exportCliCommandsFromCluster(db, endpointClusterId) { + let mapFunction = (x) => { + return { + name: x.NAME, + code: x.CODE, + mfgCode: x.MANUFACTURER_CODE, + source: x.SOURCE, + } + } + return dbApi + .dbAll( + db, + ` + SELECT + COMMAND.NAME, + COMMAND.CODE, + COMMAND.MANUFACTURER_CODE, + COMMAND.SOURCE + FROM COMMAND + INNER JOIN CLUSTER + ON COMMAND.CLUSTER_REF = CLUSTER.CLUSTER_ID + INNER JOIN PACKAGE_OPTION + ON PACKAGE_OPTION.OPTION_CODE = COMMAND.NAME + WHERE CLUSTER.CLUSTER_ID = ? + `, + [endpointClusterId] + ) + .then((rows) => rows.map(mapFunction)) +} + // exports exports.selectAllEnums = selectAllEnums exports.selectAllEnumItemsById = selectAllEnumItemsById @@ -2421,3 +2511,5 @@ exports.exportAllCommandDetailsFromEnabledClusters = exportAllCommandDetailsFrom exports.exportAllClustersDetailsIrrespectiveOfSideFromEndpointTypes = exportAllClustersDetailsIrrespectiveOfSideFromEndpointTypes exports.exportManufacturerSpecificCommandDetailsFromAllEndpointTypesAndClusters = exportManufacturerSpecificCommandDetailsFromAllEndpointTypesAndClusters exports.exportNonManufacturerSpecificCommandDetailsFromAllEndpointTypesAndClusters = exportNonManufacturerSpecificCommandDetailsFromAllEndpointTypesAndClusters +exports.exportAllCliCommandDetailsFromEnabledClusters = exportAllCliCommandDetailsFromEnabledClusters +exports.exportCliCommandsFromCluster = exportCliCommandsFromCluster diff --git a/src-electron/generator/helper-session.js b/src-electron/generator/helper-session.js index 3ef9a9986c..b3ab94f89b 100644 --- a/src-electron/generator/helper-session.js +++ b/src-electron/generator/helper-session.js @@ -284,6 +284,34 @@ function all_commands_for_user_enabled_clusters(options) { ) return promise } +/** + * This helper returns all commands which have cli within the list of enabled + * clusters. + * + * @param options + * @returns all commands with cli from the list of enabled clusters + * + */ +function all_cli_commands_for_user_enabled_clusters(options) { + let promise = queryImpexp + .exportendPointTypeIds(this.global.db, this.global.sessionId) + .then((endpointTypes) => + queryZcl.exportClustersAndEndpointDetailsFromEndpointTypes( + this.global.db, + endpointTypes + ) + ) + .then((endpointsAndClusters) => + queryZcl.exportAllCliCommandDetailsFromEnabledClusters( + this.global.db, + endpointsAndClusters + ) + ) + .then((endpointCommands) => + templateUtil.collectBlocks(endpointCommands, options, this) + ) + return promise +} /** * Creates cluster command iterator for all endpoints. @@ -356,12 +384,33 @@ function user_cluster_command_count_with_cli() { ) } +/** + * This helper works within the the cluster block helpers. It is used to get + * all commands of the cluster which have cli associated with them. + * + * param options + * Returns: all commands with cli for a cluster + * + * Example: + * {{#all_user_clusters_irrespective_of_side}} + * {{#user_cluster_commands_with_cli}} + * {{/user_cluster_commands_with_cli}} + * {{/all_user_clusters_irrespective_of_side}} + */ +function user_cluster_commands_with_cli(options) { + return queryZcl + .exportCliCommandsFromCluster(this.global.db, this.id) + .then((cliCommands) => + templateUtil.collectBlocks(cliCommands, options, this) + ) +} + /** * Creates endpoint type cluster command iterator. This works only inside * cluster block helpers. * - * @param {*} options - * @returns Promise of the resolved blocks iterating over cluster commands. + * @param options + * Returns: Promise of the resolved blocks iterating over cluster commands. */ function user_cluster_commands_all_endpoints(options) { return queryImpexp @@ -530,3 +579,5 @@ exports.all_commands_for_user_enabled_clusters = all_commands_for_user_enabled_c exports.all_user_clusters_irrespective_of_side = all_user_clusters_irrespective_of_side exports.all_user_cluster_manufacturer_specific_commands = all_user_cluster_manufacturer_specific_commands exports.all_user_cluster_non_manufacturer_specific_commands = all_user_cluster_non_manufacturer_specific_commands +exports.user_cluster_commands_with_cli = user_cluster_commands_with_cli +exports.all_cli_commands_for_user_enabled_clusters = all_cli_commands_for_user_enabled_clusters