Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / app / zap-templates / templates / chip / helper.js
1 /*
2  *
3  *    Copyright (c) 2020-2021 Project CHIP Authors
4  *
5  *    Licensed under the Apache License, Version 2.0 (the "License");
6  *    you may not use this file except in compliance with the License.
7  *    You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *    Unless required by applicable law or agreed to in writing, software
12  *    distributed under the License is distributed on an "AS IS" BASIS,
13  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *    See the License for the specific language governing permissions and
15  *    limitations under the License.
16  */
17
18 // Import helpers from zap core
19 const zapPath      = '../../../../../third_party/zap/repo/src-electron/';
20 const queryConfig  = require(zapPath + 'db/query-config.js')
21 const queryImpexp  = require(zapPath + 'db/query-impexp.js')
22 const templateUtil = require(zapPath + 'generator/template-util.js')
23 const zclHelper    = require(zapPath + 'generator/helper-zcl.js')
24 const zclQuery     = require(zapPath + 'db/query-zcl.js')
25
26 const StringHelper    = require('../../common/StringHelper.js');
27 const ChipTypesHelper = require('../../common/ChipTypesHelper.js');
28
29 /**
30  * This method converts a ZCL type to the length expected for the
31  * BufferWriter.Put method.
32  * TODO
33  * Not all types are supported at the moment, so if there is any unsupported type
34  * that we are trying to convert, it will throw an error.
35  */
36 function asPutLength(zclType)
37 {
38   const type = ChipTypesHelper.asBasicType(zclType);
39   switch (type) {
40   case 'int8_t':
41   case 'int16_t':
42   case 'int32_t':
43   case 'int64_t':
44   case 'uint8_t':
45   case 'uint16_t':
46   case 'uint32_t':
47   case 'uint64_t':
48     return type.replace(/[^0-9]/g, '');
49   default:
50     throw error = 'asPutLength: Unhandled type: ' + zclType;
51   }
52 }
53
54 function asPutCastType(zclType)
55 {
56   const type = ChipTypesHelper.asBasicType(zclType);
57   switch (type) {
58   case 'int8_t':
59   case 'int16_t':
60   case 'int32_t':
61   case 'int64_t':
62     return 'u' + type;
63   case 'uint8_t':
64   case 'uint16_t':
65   case 'uint32_t':
66   case 'uint64_t':
67     return type;
68   default:
69     throw error = 'asPutCastType: Unhandled type: ' + zclType;
70   }
71 }
72
73 function getEnabledClustersForSide(clusters, side)
74 {
75   clusters = clusters.filter(cluster => cluster.enabled == 1);
76
77   if (side == 'all') {
78     return clusters;
79   }
80
81   return clusters.filter(cluster => cluster.side == side);
82 }
83
84 function getClusters(options, side)
85 {
86   const db        = this.global.db;
87   const sessionId = this.global.sessionId;
88
89   return queryImpexp.exportendPointTypeIds(db, sessionId)
90       .then(endpointTypes => zclQuery.exportAllClustersDetailsFromEndpointTypes(db, endpointTypes))
91       .then(clusters => getEnabledClustersForSide(clusters, side));
92 }
93
94 function getClustersAsBlocks(options, side)
95 {
96   function fn(pkgId)
97   {
98     return getClusters.call(this, options, side).then(clusters => templateUtil.collectBlocks(clusters, options, this))
99   }
100
101   return templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => console.log(err));
102 }
103
104 /**
105  * Creates block iterator over the enabled server side clusters
106  *
107  * @param {*} options
108  */
109 function chip_server_clusters(options)
110 {
111   return getClustersAsBlocks.call(this, options, 'server');
112 }
113
114 /**
115  * Check if there is any enabled server clusters
116  *
117  */
118 function chip_has_server_clusters(options)
119 {
120   let promise = getClusters.call(this, options, 'server').then(clusters => !!clusters.length);
121   return templateUtil.templatePromise(this.global, promise);
122 }
123
124 /**
125  * Creates block iterator over client side enabled clusters
126  *
127  * @param {*} options
128  */
129 function chip_client_clusters(options)
130 {
131   return getClustersAsBlocks.call(this, options, 'client');
132 }
133
134 /**
135  * Check if there is any enabled client clusters
136  *
137  */
138 function chip_has_client_clusters(options)
139 {
140   let promise = getClusters.call(this, options, 'client').then(clusters => !!clusters.length);
141   return templateUtil.templatePromise(this.global, promise);
142 }
143
144 /**
145  * Creates block iterator over enabled clusters
146  *
147  * @param {*} options
148  */
149 function chip_clusters(options)
150 {
151   return getClustersAsBlocks.call(this, options, 'all');
152 }
153
154 /**
155  * Check if there is any enabled clusters
156  *
157  */
158 function chip_has_clusters(options)
159 {
160   let promise = getClusters.call(this, options, 'client').then(clusters => !!clusters.length);
161   return templateUtil.templatePromise(this.global, promise);
162 }
163
164 /**
165  * Creates block iterator over the server side cluster command
166  * for a given cluster.
167  *
168  * This function is meant to be used inside a {{#chip_server_clusters}}
169  * block. It will throws otherwise.
170  *
171  * @param {*} options
172  */
173 function chip_server_cluster_commands(options)
174 {
175   // Retrieve the clusterName and the clusterSide. If any if not available, an error will be thrown.
176   const clusterName = this.name;
177   const clusterSide = this.side;
178   if (clusterName == undefined || clusterSide == undefined) {
179     const error = 'chip_server_cluster_commands: Could not find relevant parent cluster.';
180     console.log(error);
181     throw error;
182   }
183
184   function filterCommand(cmd)
185   {
186     return cmd.clusterName == clusterName && cmd.clusterSide == 'client' && cmd.name.includes('Response') == false;
187   }
188
189   const db = this.global.db;
190   return queryImpexp.exportendPointTypeIds(db, this.global.sessionId)
191       .then(endpointTypes => zclQuery.exportClustersAndEndpointDetailsFromEndpointTypes(db, endpointTypes))
192       .then(endpointsAndClusters => zclQuery.exportCommandDetailsFromAllEndpointTypesAndClusters(db, endpointsAndClusters))
193       .then(endpointCommands => endpointCommands.filter(filterCommand))
194       .then(endpointCommands => templateUtil.collectBlocks(endpointCommands, options, this))
195 }
196
197 /**
198  * Creates block iterator over the server side cluster command arguments
199  * for a given command.
200  *
201  * This function is meant to be used inside a {{#chip_server_cluster_commands}}
202  * block. It will throws otherwise.
203  *
204  * @param {*} options
205  */
206 function chip_server_cluster_command_arguments(options)
207 {
208   const db = this.global.db;
209
210   function collectItem(arg, pkgId)
211   {
212     return zclHelper.isStruct(db, arg.type, pkgId).then(result => {
213       if (result == 'unknown') {
214         return arg;
215       }
216
217       return zclQuery.selectStructByName(db, arg.type, pkgId).then(rec => {
218         return zclQuery.selectAllStructItemsById(db, rec.id).then(items => items.map(item => {
219           item.name = item.label;
220           return item;
221         }));
222       })
223     })
224   }
225
226   function collectItems(args, pkgId)
227   {
228     return Promise.all(args.map(arg => collectItem.call(this, arg, pkgId))).then(items => items.flat()).then(items => {
229       return Promise.all(items.map(item => {
230         if (StringHelper.isString(item.type)) {
231           item.chipType = 'chip::ByteSpan';
232           return item;
233         }
234
235         return zclHelper.asUnderlyingZclType.call(this, item.type, options).then(zclType => {
236           // Enhanced the command argument with 'chipType', 'chipTypePutLength', 'chipTypePutCastType' for conveniences.
237           item.chipType            = zclType;
238           item.chipTypePutLength   = asPutLength(zclType);
239           item.chipTypePutCastType = asPutCastType(zclType);
240           return item;
241         })
242       }));
243     });
244   }
245
246   function fn(pkgId)
247   {
248     return zclQuery.selectCommandArgumentsByCommandId(db, this.id, pkgId)
249         .then(args => collectItems.call(this, args, pkgId))
250         .then(items => templateUtil.collectBlocks(items, options, this));
251   }
252
253   const promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => console.log(err));
254   return templateUtil.templatePromise(this.global, promise)
255 }
256
257 /**
258  * Returns if a given command argument chip type is signed.
259  *
260  * This function is meant to be used inside a {{#chip_*}} block.
261  * It will throws otherwise.
262  *
263  * @param {*} options
264  */
265 function isSignedType()
266 {
267   const type = this.chipType;
268   if (!type) {
269     const error = 'isSignedType: Could not find chipType';
270     console.log(error);
271     throw error;
272   }
273
274   switch (type) {
275   case 'int8_t':
276   case 'int16_t':
277   case 'int32_t':
278   case 'int64_t':
279     return true;
280   default:
281     return false;
282   }
283 }
284
285 /**
286  * Returns if a given command argument chip type is discrete.
287  *
288  * This function is meant to be used inside a {{#chip_*}} block.
289  * It will throws otherwise.
290  *
291  * @param {*} options
292  */
293 function isDiscreteType()
294 {
295   const type = this.chipType;
296   if (!type) {
297     const error = 'isDiscreteType: Could not find chipType';
298     console.log(error);
299     throw error;
300   }
301
302   return this.discrete;
303 }
304
305 function getAttributes(pkgId, options)
306 {
307   const db = this.global.db;
308   return queryConfig.getAllSessionAttributes(db, this.global.sessionId).then(atts => {
309     return Promise.all(atts.map(att => zclQuery.selectAtomicByName(db, att.type, pkgId).then(atomic => {
310       // Enhanced the attribute with 'atomidId', 'discrete', chipType properties for convenience.
311       att.atomicTypeId = atomic.atomicId;
312       att.discrete     = atomic.discrete;
313
314       if (StringHelper.isString(att.type)) {
315         // Enhanced the command argument with 'chipType' for conveniences.
316         att.chipType = 'chip::ByteSpan';
317         return att;
318       }
319
320       return zclHelper.asUnderlyingZclType.call(this, att.type, options).then(zclType => {
321         att.chipType = zclType;
322         return att;
323       });
324     })));
325   })
326 }
327
328 /**
329  * Creates block iterator over the server side cluster attributes
330  * for a given cluster.
331  *
332  * This function is meant to be used inside a {{#chip_server_clusters}}
333  * block. It will throws otherwise.
334  *
335  * @param {*} options
336  */
337 function chip_server_cluster_attributes(options)
338 {
339   // Retrieve the clusterCode and the clusterSide. If any if not available, an error will be thrown.
340   const clusterCode = this.code;
341   const clusterSide = this.side;
342   if (clusterCode == undefined || clusterSide == undefined) {
343     const error = 'chip_server_cluster_attributes: Could not find relevant parent cluster.';
344     console.log(error);
345     throw error;
346   }
347
348   function fn(pkgId)
349   {
350     return getAttributes.call(this, pkgId, options).then(atts => {
351       atts = atts.filter(att => att.clusterCode == clusterCode && att.side == 'server');
352       atts.forEach(att => {
353         const sameAttributes = atts.filter(att2 => att.name == att2.name);
354         let isWritable       = !!sameAttributes.find(att2 => att2.writable);
355         let isReportable     = !!sameAttributes.find(att2 => att2.reportable.included);
356         if (isWritable || isReportable) {
357           if (!StringHelper.isString(att.type)) {
358             att.chipTypePutLength   = asPutLength(att.chipType);
359             att.chipTypePutCastType = asPutCastType(att.chipType);
360           }
361           att.writable            = isWritable;
362           att.reportable.included = isReportable;
363         }
364       })
365       atts = atts.filter((att, index) => atts.findIndex(att2 => att.name == att2.name) == index);
366       return templateUtil.collectBlocks(atts, options, this);
367     })
368   }
369
370   const promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => console.log(err));
371   return templateUtil.templatePromise(this.global, promise);
372 }
373
374 /**
375  * Returns if a given attribute is writable.
376  *
377  * This function is meant to be used inside a {{#chip_server_cluster_attributes}} block.
378  * It will throws otherwise.
379  *
380  * @param {*} options
381  */
382 function isWritableAttribute(options)
383 {
384   if (this.attributeCode == undefined) {
385     const error = 'isWritableAttribute: missing attribute code.';
386     console.log(error);
387     throw error;
388   }
389
390   return this.writable == 1;
391 }
392
393 /**
394  * Returns if a given attribute is reportable.
395  *
396  * This function is meant to be used inside a {{#chip_server_cluster_attributes}} block.
397  * It will throws otherwise.
398  *
399  * @param {*} options
400  */
401 function isReportableAttribute(options)
402 {
403   if (this.attributeCode == undefined) {
404     const error = 'isReportableAttribute: missing attribute code.';
405     console.log(error);
406     throw error;
407   }
408
409   return this.reportable.included == 1;
410 }
411
412 /**
413  * Returns if a given command is manufacturer specific
414  *
415  * This function is meant to be used inside a {{#chip_server_cluster_commands}} block.
416  * It will throws otherwise.
417  *
418  * @param {*} options
419  */
420 function isManufacturerSpecificCommand()
421 {
422   if (this.commandSource == undefined) {
423     const error = 'isManufacturerSpecificCommand: Not inside a ({#chip_server_cluster_commands}} block.';
424     console.log(error);
425     throw error;
426   }
427
428   return !!this.mfgCode;
429 }
430
431 function asPythonType(zclType)
432 {
433   const type = ChipTypesHelper.asBasicType(zclType);
434   switch (type) {
435   case 'int8_t':
436   case 'int16_t':
437   case 'int32_t':
438   case 'int64_t':
439   case 'uint8_t':
440   case 'uint16_t':
441   case 'uint32_t':
442   case 'uint64_t':
443     return 'int';
444   case 'char *':
445     return 'str';
446   case 'uint8_t *':
447   case 'chip::ByteSpan':
448     return 'bytes'
449   }
450 }
451
452 function asPythonCType(zclType)
453 {
454   const type = ChipTypesHelper.asBasicType(zclType);
455   switch (type) {
456   case 'int8_t':
457   case 'int16_t':
458   case 'int32_t':
459   case 'int64_t':
460   case 'uint8_t':
461   case 'uint16_t':
462   case 'uint32_t':
463   case 'uint64_t':
464     return 'c_' + type.replace('_t', '');
465   case 'char *':
466   case 'uint8_t *':
467     return 'c_char_p';
468   }
469 }
470
471 function hasSpecificResponse(commandName)
472 {
473   // Retrieve the clusterName and the clusterSide. If any if not available, an error will be thrown.
474   const clusterName = this.parent.name;
475   const clusterSide = this.parent.side;
476   if (clusterName == undefined || clusterSide == undefined) {
477     const error = 'chip_server_cluster_commands: Could not find relevant parent cluster.';
478     console.log(error);
479     throw error;
480   }
481
482   function filterCommand(cmd)
483   {
484     return cmd.clusterName == clusterName && cmd.name == (commandName + "Response");
485   }
486
487   function fn(pkgId)
488   {
489     const db = this.global.db;
490     return queryImpexp.exportendPointTypeIds(db, this.global.sessionId)
491         .then(endpointTypes => zclQuery.exportClustersAndEndpointDetailsFromEndpointTypes(db, endpointTypes))
492         .then(endpointsAndClusters => zclQuery.exportCommandDetailsFromAllEndpointTypesAndClusters(db, endpointsAndClusters))
493         .then(endpointCommands => endpointCommands.filter(filterCommand).length)
494   }
495
496   const promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => console.log(err));
497   return templateUtil.templatePromise(this.global, promise);
498 }
499
500 function asCallbackAttributeType(attributeType)
501 {
502   switch (parseInt(attributeType)) {
503   case 0x00: // nodata / No data
504   case 0x0A: // data24 / 24-bit data
505   case 0x0C: // data40 / 40-bit data
506   case 0x0D: // data48 / 48-bit data
507   case 0x0E: // data56 / 56-bit data
508   case 0x1A: // map24 / 24-bit bitmap
509   case 0x1C: // map40 / 40-bit bitmap
510   case 0x1D: // map48 / 48-bit bitmap
511   case 0x1E: // map56 / 56-bit bitmap
512   case 0x22: // uint24 / Unsigned 24-bit integer
513   case 0x24: // uint40 / Unsigned 40-bit integer
514   case 0x25: // uint48 / Unsigned 48-bit integer
515   case 0x26: // uint56 / Unsigned 56-bit integer
516   case 0x2A: // int24 / Signed 24-bit integer
517   case 0x2C: // int40 / Signed 40-bit integer
518   case 0x2D: // int48 / Signed 48-bit integer
519   case 0x2E: // int56 / Signed 56-bit integer
520   case 0x38: // semi / Semi-precision
521   case 0x39: // single / Single precision
522   case 0x3A: // double / Double precision
523   case 0x48: // array / Array
524   case 0x49: // struct / Structure
525   case 0x50: // set / Set
526   case 0x51: // bag / Bag
527   case 0xE0: // ToD / Time of day
528   case 0xEA: // bacOID / BACnet OID
529   case 0xF1: // key128 / 128-bit security key
530   case 0xFF: // unk / Unknown
531     return 'Unsupported';
532   case 0x41: // octstr / Octet string
533   case 0x42: // string / Character string
534   case 0x43: // octstr16 / Long octet string
535   case 0x44: // string16 / Long character string
536     return 'String';
537   case 0x08: // data8 / 8-bit data
538   case 0x18: // map8 / 8-bit bitmap
539   case 0x20: // uint8 / Unsigned  8-bit integer
540   case 0x30: // enum8 / 8-bit enumeration
541     return 'Int8u';
542   case 0x09: // data16 / 16-bit data
543   case 0x19: // map16 / 16-bit bitmap
544   case 0x21: // uint16 / Unsigned 16-bit integer
545   case 0x31: // enum16 / 16-bit enumeration
546   case 0xE8: // clusterId / Cluster ID
547   case 0xE9: // attribId / Attribute ID
548     return 'Int16u';
549   case 0x0B: // data32 / 32-bit data
550   case 0x1B: // map32 / 32-bit bitmap
551   case 0x23: // uint32 / Unsigned 32-bit integer
552   case 0xE1: // date / Date
553   case 0xE2: // UTC / UTCTime
554     return 'Int32u';
555   case 0x0F: // data64 / 64-bit data
556   case 0x1F: // map64 / 64-bit bitmap
557   case 0x27: // uint64 / Unsigned 64-bit integer
558   case 0xF0: // EUI64 / IEEE address
559     return 'Int64u';
560   case 0x10: // bool / Boolean
561     return 'Boolean';
562   case 0x28: // int8 / Signed 8-bit integer
563     return 'Int8s';
564   case 0x29: // int16 / Signed 16-bit integer
565     return 'Int16s';
566   case 0x2B: // int32 / Signed 32-bit integer
567     return 'Int32s';
568   case 0x2F: // int64 / Signed 64-bit integer
569     return 'Int64s';
570   default:
571     error = 'Unhandled attribute type ' + attributeType;
572     throw error;
573   }
574 }
575
576 function asObjectiveCBasicType(type)
577 {
578   if (StringHelper.isOctetString(type)) {
579     return 'NSData *';
580   } else if (StringHelper.isCharString(type)) {
581     return 'NSString *';
582   } else {
583     return ChipTypesHelper.asBasicType(this.chipType);
584   }
585 }
586
587 function asObjectiveCNumberType(label, type)
588 {
589   function fn(pkgId)
590   {
591     const options = { 'hash' : {} };
592     return zclHelper.asUnderlyingZclType.call(this, type, options).then(zclType => {
593       switch (zclType) {
594       case 'uint8_t':
595         return 'UnsignedChar';
596       case 'uint16_t':
597         return 'UnsignedShort';
598       case 'uint32_t':
599         return 'UnsignedLong';
600       case 'uint64_t':
601         return 'UnsignedLongLong';
602       case 'int8_t':
603         return 'Char';
604       case 'int16_t':
605         return 'Short';
606       case 'int32_t':
607         return 'Long';
608       case 'int64_t':
609         return 'LongLong';
610       default:
611         error = label + ': Unhandled underlying type ' + zclType + ' for original type ' + type;
612         throw error;
613       }
614     })
615   }
616
617   const promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => console.log(err));
618   return templateUtil.templatePromise(this.global, promise)
619 }
620
621 function isStrEndsWith(str, substr)
622 {
623   return str.endsWith(substr);
624 }
625
626 //
627 // Module exports
628 //
629 exports.chip_clusters                         = chip_clusters;
630 exports.chip_has_clusters                     = chip_has_clusters;
631 exports.chip_client_clusters                  = chip_client_clusters;
632 exports.chip_has_client_clusters              = chip_has_client_clusters;
633 exports.chip_server_clusters                  = chip_server_clusters;
634 exports.chip_has_server_clusters              = chip_has_server_clusters;
635 exports.chip_server_cluster_commands          = chip_server_cluster_commands;
636 exports.chip_server_cluster_command_arguments = chip_server_cluster_command_arguments
637 exports.asBasicType                           = ChipTypesHelper.asBasicType;
638 exports.asObjectiveCBasicType                 = asObjectiveCBasicType;
639 exports.asObjectiveCNumberType                = asObjectiveCNumberType;
640 exports.isSignedType                          = isSignedType;
641 exports.isDiscreteType                        = isDiscreteType;
642 exports.chip_server_cluster_attributes        = chip_server_cluster_attributes;
643 exports.isWritableAttribute                   = isWritableAttribute;
644 exports.isReportableAttribute                 = isReportableAttribute;
645 exports.isManufacturerSpecificCommand         = isManufacturerSpecificCommand;
646 exports.asPythonType                          = asPythonType;
647 exports.asPythonCType                         = asPythonCType;
648 exports.asCallbackAttributeType               = asCallbackAttributeType;
649 exports.hasSpecificResponse                   = hasSpecificResponse;
650 exports.isStrEndsWith                         = isStrEndsWith;