Skip to content
代码片段 群组 项目
helper-endpointconfig.js 37.7 KB
更新 更旧
  • 了解如何忽略特定修订
  • /**
     *
     *    Copyright (c) 2020 Silicon Labs
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *        http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    
    
    const cHelper = require('./helper-c.js')
    
    const templateUtil = require('./template-util')
    
    const queryEndpoint = require('../db/query-endpoint.js')
    
    const queryEndpointType = require('../db/query-endpoint-type.js')
    
    Jing Teng's avatar
    Jing Teng 已提交
    const bin = require('../util/bin')
    
    Timotej Ecimovic's avatar
    Timotej Ecimovic 已提交
    const zclUtil = require('../util/zcl-util.js')
    
    const dbEnum = require('../../src-shared/db-enum.js')
    
    /**
     * Returns number of endpoint types.
     *
     * @param {*} options
     * @returns number of endpoint types
     */
    
    function endpoint_type_count(options) {
    
      return this.endpointTypes.length
    
    /**
     * Returns number of endpoints.
     *
     * @param {*} options
     * @returns number of endpoints
     */
    
    function endpoint_count(options) {
      return this.endpoints.length
    }
    
    
    /**
     * Prints out all the macros that the endpoint config
     * configuration depends on. These macros are created
     * by ZAP, because the use of these macros is also
     * created by ZAP.
     *
     * @returns Macros that need to be created
     */
    function endpoint_config_macros(options) {
    
      let longDef = options.hash.longDefaults
      let minMaxDef = options.hash.minMaxDefaults
      if (longDef == null) longDef = 'def_long_defaults'
      if (minMaxDef == null) minMaxDef = 'def_minmax_defaults'
    
    
      return `
    #define ZAP_TYPE(type) ZCL_ ## type ## _ATTRIBUTE_TYPE
    #define ZAP_LONG_DEFAULTS_INDEX(index) {(uint8_t*)(&${longDef}[index])}
    #define ZAP_MIN_MAX_DEFAULTS_INDEX(index) {(uint8_t*)(&${minMaxDef}[index])}
    #define ZAP_EMPTY_DEFAULT() {(uint8_t*) 0}
    #define ZAP_SIMPLE_DEFAULT(x) {(uint8_t *) x}
    `
    }
    
    
    /**
     * Creates array of endpointId fields on endpoints
     *
     * @param {*} options
     * @returns C array including the { } brackets
     */
    
    function endpoint_fixed_endpoint_array(options) {
    
      this.endpoints.forEach((ep) => {
        epIds.push('0x' + bin.int16ToHex(ep.endpointId))
      })
      return '{ ' + epIds.join(', ') + ' }'
    }
    
    
    /**
     * Creates array of profileId fields on endpoints
     *
     * @param {*} options
     * @returns C array including the { } brackets
     */
    
    function endpoint_fixed_profile_id_array(options) {
    
      let profileIds = []
    
      this.endpoints.forEach((ep) => {
    
        profileIds.push('0x' + bin.int16ToHex(parseInt(ep.profileId)))
    
      })
      return '{ ' + profileIds.join(', ') + ' }'
    }
    
    
    /**
     * Creates array of networkId fields on endpoints
     *
     * @param {*} options
     * @returns C array including the { } brackets
     */
    function endpoint_fixed_network_array(options) {
    
      return '{ ' + this.endpoints.map((ep) => ep.networkId).join(', ') + ' }'
    
    /**
     * Each element of an array contains an index into the
     * endpoint type array, for the appropriate endpoint.
     *
     * @param {*} options
     * @returns C array of indexes, one for each endpoint.
     */
    function endpoint_fixed_endpoint_type_array(options) {
    
    Timotej Ecimovic's avatar
    Timotej Ecimovic 已提交
      for (const ep of this.endpoints) {
        let epType = ep.endpointTypeRef
    
        let index = -1
        for (let j = 0; j < this.endpointTypes.length; j++) {
    
          if (epType == this.endpointTypes[j].id) {
            index = j
          }
        }
        indexes.push(index)
      }
      return '{ ' + indexes.join(', ') + ' }'
    }
    
    
    function createMfgCodes(codeIndexPairs) {
    
      let ret = '{ \\\n'
    
      if (codeIndexPairs.length == 0) {
        ret = ret.concat('  { 0x00, 0x00 } \\\n')
      } else {
        codeIndexPairs.forEach((c) => {
    
    jfpenven's avatar
    jfpenven 已提交
          ret = ret.concat(`  { ${c.index}, ${c.mfgCode} },\\\n`)
    
        })
      }
      return ret.concat('}\n')
    }
    
    /**
     * Generates array of { index , mfgCode } pairs, matching
     * the indexes in attribute table.
     *
     * @param {*} options
     * @returns manufacturer code array
     */
    function endpoint_attribute_manufacturer_codes(options) {
      return createMfgCodes(this.attributeMfgCodes)
    }
    
    function endpoint_attribute_manufacturer_code_count(options) {
      return this.attributeMfgCodes.length
    }
    
    function endpoint_command_manufacturer_codes(options) {
      return createMfgCodes(this.commandMfgCodes)
    }
    
    function endpoint_command_manufacturer_code_count(options) {
      return this.commandMfgCodes.length
    }
    
    function endpoint_cluster_manufacturer_codes(options) {
      return createMfgCodes(this.clusterMfgCodes)
    }
    
    function endpoint_cluster_manufacturer_code_count(options) {
      return this.clusterMfgCodes.length
    }
    
    
    function endpoint_largest_attribute_size(options) {
    
      return this.largestAttribute + 1
    
    }
    
    function endpoint_singletons_size(options) {
      return this.singletonsSize
    }
    
    function endpoint_total_storage_size(options) {
      return this.totalAttributeSize
    }
    
    
    function endpoint_command_count(options) {
      return this.commandList.length
    }
    
    
    function endpoint_types_list(options) {
    
      let ret = '{ \\\n'
    
      this.endpointList.forEach((ep) => {
        ret = ret.concat(
    
          `  { ZAP_CLUSTER_INDEX(${ep.clusterIndex}), ${ep.clusterCount}, ${ep.attributeSize} }, \\\n`
    
        )
      })
      return ret.concat('}\n')
    }
    
    
    function endpoint_cluster_count(options) {
      return this.clusterList.length
    }
    
    
    function endpoint_cluster_list(options) {
    
      let ret = '{ \\\n'
    
      this.clusterList.forEach((c) => {
    
        if (c.mask.length == 0) {
          mask = '0'
        } else {
          mask = c.mask
            .map((m) => `ZAP_CLUSTER_MASK(${m.toUpperCase()})`)
            .join(' | ')
        }
    
          `  { ${c.clusterId}, ZAP_ATTRIBUTE_INDEX(${c.attributeIndex}), ${c.attributeCount}, ${c.attributeSize}, ${mask}, ${c.functions} }, /* ${c.comment} */ \\\n`
    
    function endpoint_command_list(options) {
    
      let ret = '{ \\\n'
    
      this.commandList.forEach((cmd) => {
    
        if (cmd.comment != comment) {
          ret += `\\\n  /* ${cmd.comment} */ \\\n`
          comment = cmd.comment
        }
    
    
        if (cmd.mask.length == 0) {
          mask = '0'
        } else {
          mask = cmd.mask
            .map((m) => `ZAP_COMMAND_MASK(${m.toUpperCase()})`)
            .join(' | ')
        }
    
        ret += `  { ${cmd.clusterId}, ${cmd.commandId}, ${mask} }, /* ${cmd.name} */ \\\n`
    
    function endpoint_attribute_count(options) {
      return this.attributeList.length
    }
    
    
    function endpoint_attribute_list(options) {
    
      let order = options.hash.order
      if (order == null || order.length == 0) {
        // This is the default value if none is specified
        order = 'default,id,size,type,mask'
      }
    
      let littleEndian = true
      let pointerSize = 4
      if (options.hash.endian == 'big') {
        littleEndian = false
        if (typeof options.hash.pointer != 'undefined') {
          pointerSize = options.hash.pointer
        }
      }
    
    
      let ret = '{ \\\n'
    
      this.attributeList.forEach((at) => {
    
        if (at.comment != comment) {
          ret += `\\\n  /* ${at.comment} */ \\\n`
          comment = at.comment
        }
    
    
        if (at.mask.length == 0) {
          mask = '0'
        } else {
          mask = at.mask
            .map((m) => `ZAP_ATTRIBUTE_MASK(${m.toUpperCase()})`)
            .join(' | ')
        }
    
        // If no default value is found, default to 0
    
        if (!at.defaultValue) {
    
          finalDefaultValue = `ZAP_EMPTY_DEFAULT()`
        } else if (at.isMacro) {
          finalDefaultValue = at.defaultValue
        } else {
    
          let defaultValue = at.defaultValue
          if (!littleEndian) {
    
            defaultValue = Number(defaultValue)
              .toString(16)
              .padStart(6, '0x0000')
              .padEnd(2 + 2 * pointerSize, '0')
    
          }
          finalDefaultValue = `ZAP_SIMPLE_DEFAULT(${defaultValue})`
    
        let orderTokens = order.split(',').map((x) => (x ? x.trim() : ''))
        let items = []
        orderTokens.forEach((tok) => {
          switch (tok) {
            case 'default':
              items.push(finalDefaultValue)
              break
            case 'id':
              items.push(at.id)
              break
            case 'size':
              items.push(at.size)
              break
            case 'type':
              items.push(at.type)
              break
            case 'mask':
              items.push(mask)
              break
          }
        })
    
        ret += `  { ${items.join(', ')} }, /* ${at.name} */  \\\n`
    
    function endpoint_fixed_device_type_array(options) {
      let ret = '{'
      let wroteItem = false
    
      for (let i = 0; i < this.deviceList.length; i++) {
        if (this.deviceList[i] != null && this.deviceList[i].deviceId != null) {
          if (wroteItem) {
            ret += ','
          }
    
          ret +=
            '{' +
            '0x' +
            bin.int16ToHex(this.deviceList[i].deviceId) +
            ',' +
            this.deviceList[i].deviceVersion.toString() +
            '}'
          wroteItem = true
        }
      }
    
      ret += '}'
      return ret
    
    function endpoint_fixed_device_type_array_offsets(options) {
      let ret = '{ '
      let curOffset = 0
    
      for (let i = 0; i < this.deviceList.length; i++) {
        if (i != 0) {
          ret += ','
        }
    
        ret += curOffset.toString()
    
        if (this.deviceList[i] != null && this.deviceList[i].deviceId != null) {
          curOffset += 1
        }
      }
    
      ret += '}'
      return ret
    }
    
    function endpoint_fixed_device_type_array_lengths(options) {
      let ret = '{ '
    
      for (let i = 0; i < this.deviceList.length; i++) {
        if (i != 0) {
          ret += ','
        }
    
        if (this.deviceList[i] != null && this.deviceList[i].deviceId != null) {
          ret += '1'
        } else {
          ret += '0'
        }
      }
    
      ret += '}'
      return ret
    
    function endpoint_attribute_min_max_count(options) {
      return this.minMaxList.length
    }
    
    function endpoint_attribute_min_max_list(options) {
    
      let order = options.hash.order
      if (order == null || order.length == 0) {
        order = 'def,min,max'
      }
    
      let ret = '{ \\\n'
    
      this.minMaxList.forEach((mm, index) => {
    
        if (mm.typeSize > 2) {
    
          throw new Error(
            `Can't have min/max for attributes larger than 2 bytes like '${mm.name}'`
          )
    
        if (mm.comment != comment) {
          ret += `\\\n  /* ${mm.comment} */ \\\n`
          comment = mm.comment
        }
    
    
        let def = parseInt(mm.default)
        let min = parseInt(mm.min)
        let max = parseInt(mm.max)
    
        if (isNaN(def)) def = 0
        if (isNaN(min)) min = 0
        if (isNaN(max)) max = 0xffff
    
        let defS =
          (def >= 0 ? '' : '-') + '0x' + Math.abs(def).toString(16).toUpperCase()
        let minS =
          (min >= 0 ? '' : '-') + '0x' + Math.abs(min).toString(16).toUpperCase()
        let maxS =
          (max >= 0 ? '' : '-') + '0x' + Math.abs(max).toString(16).toUpperCase()
    
        let defMinMaxItems = []
        order
          .split(',')
          .map((x) => (x ? x.trim() : ''))
          .forEach((tok) => {
            switch (tok) {
              case 'def':
                defMinMaxItems.push(`(uint16_t)${defS}`)
                break
              case 'min':
                defMinMaxItems.push(`(uint16_t)${minS}`)
                break
              case 'max':
                defMinMaxItems.push(`(uint16_t)${maxS}`)
                break
            }
          })
        ret += `  { ${defMinMaxItems.join(', ')} }${
    
          index == this.minMaxList.length - 1 ? '' : ','
        } /* ${mm.name} */ \\\n`
    
    /**
     * This helper supports an "order" CSV string, such as:
     *   "direction,endpoint,clusterId,attributeId,mask,mfgCode,minmax"
     * The string above is a default value, and it determines in what order are the fields generated.
     *
     * @param {*} options
     */
    
    function endpoint_reporting_config_defaults(options) {
    
      let order = options.hash.order
      if (order == null || order.length == 0) {
        // This is the default value if none is specified
        order = 'direction,endpoint,clusterId,attributeId,mask,mfgCode,minmax'
      }
      let minmaxorder = options.hash.minmaxorder
      if (minmaxorder == null || minmaxorder.length == 0) {
        minmaxorder = 'min,max,change'
      }
    
      let ret = '{ \\\n'
    
        if (r.comment != comment) {
          ret += `\\\n  /* ${r.comment} */ \\\n`
          comment = r.comment
        }
    
    
        let minmaxItems = []
        let minmaxToks = minmaxorder.split(',').map((x) => (x ? x.trim() : ''))
        minmaxToks.forEach((tok) => {
          switch (tok) {
            case 'min':
              minmaxItems.push(r.minOrSource)
              break
            case 'max':
              minmaxItems.push(r.maxOrEndpoint)
              break
            case 'change':
              minmaxItems.push(r.reportableChangeOrTimeout)
              break
          }
        })
        let minmax = minmaxItems.join(', ')
    
        if (r.mask.length == 0) {
          mask = '0'
        } else {
          mask = r.mask
            .map((m) => `ZAP_CLUSTER_MASK(${m.toUpperCase()})`)
            .join(' | ')
        }
    
        let orderTokens = order.split(',').map((x) => (x ? x.trim() : ''))
        let items = []
        orderTokens.forEach((tok) => {
          switch (tok) {
            case 'direction':
              items.push(`ZAP_REPORT_DIRECTION(${r.direction})`)
              break
            case 'endpoint':
              items.push(r.endpoint)
              break
            case 'clusterId':
              items.push(r.clusterId)
              break
            case 'attributeId':
              items.push(r.attributeId)
              break
            case 'mask':
              items.push(mask)
              break
            case 'mfgCode':
              items.push(r.mfgCode)
              break
            case 'minmax':
              items.push(`{{ ${minmax} }}`)
              break
          }
        })
    
        let singleRow = `  { ${items.join(', ')} }, /* ${r.name} */ \\\n`
        ret += singleRow
    
    }
    
    function endpoint_reporting_config_default_count(options) {
      return this.reportList.length
    }
    
    function endpoint_attribute_long_defaults_count(options) {
      return this.longDefaultsList.length
    }
    
    
    function endpoint_attribute_long_defaults(options) {
    
      let littleEndian = true
    
      if (options.hash.endian == 'big') {
        littleEndian = false
      }
    
      let ret = '{ \\\n'
    
        if (littleEndian && !types.isString(ld.type)) {
          // ld.value is in big-endian order.  For types for which endianness
          // matters, we need to reverse it.
    
          let valArr = value.split(/\s*,\s*/).filter((s) => s.length != 0)
          valArr.reverse()
          value = valArr.join(', ') + ', '
    
        if (ld.comment != comment) {
          ret += `\\\n  /* ${ld.comment}, ${
    
            littleEndian ? 'little-endian' : 'big-endian'
    
        ret += `  /* ${ld.index} - ${ld.name}, */\\\n  ${value}\\\n\\\n`
    
    function asMEI(manufacturerCode, code) {
    
      // Left-shift (and for that matter bitwise or) produces a _signed_ 32-bit
      // number, which will probably be negative.  Force it to unsigned 32-bit using
      // >>> 0.
    
      return '0x' + bin.int32ToHex(((manufacturerCode << 16) | code) >>> 0)
    
    // The representation of null depends on the type, so we can't use a single
    // macro that's defined elsewhere for "null value".
    function determineAttributeDefaultValue(
      specifiedDefault,
      type,
      typeSize,
      isNullable
    ) {
      if (specifiedDefault !== null || !isNullable) {
        return specifiedDefault
      }
    
      if (types.isString(type)) {
        // Handled elsewhere.
        return null
      }
    
      if (types.isSignedInteger(type)) {
        return '0x80' + '00'.repeat(typeSize - 1)
      }
    
      if (types.isFloat(type)) {
        // Not supported yet.
        throw new Error(
          "Don't know how to output a null default value for a float type"
        )
      }
    
      // Assume everything else is an unsigned integer.
      return '0x' + 'FF'.repeat(typeSize)
    }
    
    
    /**
     * Attribute collection works like this:
     *    1.) Go over all the clusters that exist.
     *    2.) If client is included on at least one endpoint add client atts.
     *    3.) If server is included on at least one endpoint add server atts.
     */
    
    async function collectAttributes(endpointTypes, options) {
    
      let commandMfgCodes = [] // Array of { index, mfgCode } objects
      let clusterMfgCodes = [] // Array of { index, mfgCode } objects
      let attributeMfgCodes = [] // Array of { index, mfgCode } objects
    
      let attributeList = []
      let commandList = []
      let endpointList = [] // Array of { clusterIndex, clusterCount, attributeSize }
    
      let clusterList = [] // Array of { clusterId, attributeIndex, attributeCount, attributeSize, eventIndex, eventCount, mask, functions, comment }
    
      let longDefaults = [] // Array of strings representing bytes
      let longDefaultsIndex = 0
      let minMaxIndex = 0
      let largestAttribute = 0
      let singletonsSize = 0
      let totalAttributeSize = 0
      let clusterAttributeSize = 0
      let endpointAttributeSize = 0
      let clusterIndex = 0
      let deviceList = [] // Array of { deviceId, deviceVersion }
      let minMaxList = [] // Array of { default, min, max }
      let reportList = [] // Array of { direction, endpoint, clusterId, attributeId, mask, mfgCode, minOrSource, maxOrEndpoint, reportableChangeOrTimeout }
    
      let longDefaultsList = [] // Array of { value, size. comment }
    
      let attributeIndex = 0
    
      let spaceForDefaultValue =
        options.spaceForDefaultValue !== undefined
          ? options.spaceForDefaultValue
          : 2
    
      endpointTypes.forEach((ept) => {
    
          clusterIndex: clusterIndex,
    
          clusterCount: ept.clusters.length,
    
          attributeSize: 0,
        }
    
          deviceId: ept.deviceIdentifier,
          deviceVersion: ept.endpointVersion,
    
        endpointAttributeSize = 0
    
        // Go over all the clusters in the endpoint and add them to the list.
    
    Timotej Ecimovic's avatar
    Timotej Ecimovic 已提交
        ept.clusters.sort(zclUtil.clusterComparator)
    
            endpointId: ept.endpointId,
    
            clusterId: asMEI(c.manufacturerCode, c.code),
    
            attributeIndex: attributeIndex,
            attributeCount: c.attributes.length,
    
            attributeSize: 0,
    
            eventIndex: eventIndex,
            eventCount: c.events.length,
    
            mask: [],
    
            comment: `Endpoint: ${ept.endpointId}, Cluster: ${c.name} (${c.side})`,
    
          clusterAttributeSize = 0
    
          cluster.mask.push(c.side)
    
          attributeIndex += c.attributes.length
    
          eventIndex += c.events.length
    
    Timotej Ecimovic's avatar
    Timotej Ecimovic 已提交
          c.attributes.sort(zclUtil.attributeComparator)
    
          // Go over all the attributes in the endpoint and add them to the list.
          c.attributes.forEach((a) => {
    
            // typeSize is the size of a buffer needed to hold the attribute, if
            // that's known.
    
            // defaultSize is the size of the attribute in the readonly defaults
            // store.
    
            let defaultSize = typeSize
    
            let attributeDefaultValue = determineAttributeDefaultValue(
              a.defaultValue,
              a.type,
              typeSize,
              a.isNullable
            )
    
            // Various types store the length of the actual content in bytes.
            // For those, we can size the default storage to be just big enough for
            // the actual default value.
    
            if (types.isOneBytePrefixedString(a.type)) {
    
              defaultSize =
                (attributeDefaultValue ? attributeDefaultValue.length : 0) + 1
    
            } else if (types.isTwoBytePrefixedString(a.type)) {
    
              defaultSize =
                (attributeDefaultValue ? attributeDefaultValue.length : 0) + 2
    
            // storageSize is the size of the attribute in the read/write attribute
            // store.
            let storageSize = typeSize
            // External attributes should not take up space in the default store or
            // the read/write store.
    
            if (a.storage == dbEnum.storageOption.external) {
    
              // Some external attributes do not have a usable typeSize
              // (e.g. structs or lists of structs); the value of typeSize in those
              // cases is an error string.  Use 0 in those cases.
              if (typeof typeSize == 'string') {
                typeSize = 0
              }
              // List-typed attributes don't have a useful typeSize no matter what.
              // typeSizeAttribute will return values here based on various XML bits
              // and ZAP file default values, but all of those have nothing to do
              // with the actual attribute.
              if (a.typeInfo.atomicType == 'array') {
                typeSize = 0
              }
    
              attributeDefaultValue = undefined
    
            let defaultValueIsMacro = false
    
            // Zero-length strings can just use ZAP_EMPTY_DEFAULT() as the default
    
            // and don't need long defaults.  Similar for external strings.
    
            // Apart from that, there are a few string cases that _could_ fit into our
            // default value as the size is determined by spaceForDefaultValue.  Some
            // example strings that would fit if spaceForDefaultValue were 2 bytes are:
            // a 1-char-long short string, or a null string.  But figuring out how
            // to produce a uint8_t* for those as a literal value is a pain, so just
            // force all non-external strings with a nonempty default value to use
            // long defaults.
    
              defaultSize > spaceForDefaultValue ||
    
              (types.isString(a.type) &&
                attributeDefaultValue !== undefined &&
                attributeDefaultValue !== '')
    
              // We will need to generate the GENERATED_DEFAULTS
              longDefaults.push(a)
    
              let def
              if (
                types.isString(a.type) &&
                attributeDefaultValue === null &&
                a.isNullable
              ) {
                // We don't want to make longTypeDefaultValue know about our null
                // string representation.
                if (types.isOneBytePrefixedString(a.type)) {
                  def = ['0xFF']
                } else if (types.isTwoBytePrefixedString(a.type)) {
                  def = ['0xFF', '0xFF']
                } else {
                  throw new Error(`Unknown string type: ${type}`)
                }
              } else {
                def = types.longTypeDefaultValue(
                  defaultSize,
                  a.type,
                  attributeDefaultValue
                )
              }
    
                value: def,
    
                name: a.name,
                comment: cluster.comment,
    
                type: a.type,
    
              attributeDefaultValue = `ZAP_LONG_DEFAULTS_INDEX(${longDefaultsIndex})`
    
              longDefaultsIndex += defaultSize
    
            let mask = []
            if ((a.min != null || a.max != null) && a.isWritable) {
    
                default: attributeDefaultValue,
    
                min: a.min,
                max: a.max,
    
                name: a.name,
                comment: cluster.comment,
    
                typeSize: typeSize,
    
              attributeDefaultValue = `ZAP_MIN_MAX_DEFAULTS_INDEX(${minMaxIndex})`
    
              minMaxIndex++
    
            let rptMask = [c.side]
    
                endpoint: '0x' + bin.int16ToHex(ept.endpointId),
    
                clusterId: asMEI(c.manufacturerCode, c.code),
                attributeId: asMEI(a.manufacturerCode, a.code),
    
                mfgCode:
                  a.manufacturerCode == null
                    ? '0x0000'
                    : '0x' + bin.int16ToHex(a.manufacturerCode),
    
                minOrSource: a.minInterval,
                maxOrEndpoint: a.maxInterval,
                reportableChangeOrTimeout: a.reportableChange,
    
                name: a.name,
                comment: cluster.comment,
    
            if (typeSize > largestAttribute) {
              largestAttribute = typeSize
    
              singletonsSize += storageSize
    
            clusterAttributeSize += storageSize
            totalAttributeSize += storageSize
    
            if (a.side == dbEnum.side.client) {
              mask.push('client')
            }
    
            if (a.storage == dbEnum.storageOption.nvm) {
              mask.push('TOKENIZE')
    
            } else if (a.storage == dbEnum.storageOption.external) {
    
            } else if (a.storage == dbEnum.storageOption.ram) {
              // Nothing to do
            } else {
    
              if (!options.allowUnknownStorageOption)
                throw new Error(
                  `Unknown storage type "${a.storage}" for attribute "${
                    a.name
                  }" of cluster "${c.name}" on endpoint ${
                    ept.endpointId
                  }.  Valid values are: ${[
                    dbEnum.storageOption.nvm,
                    dbEnum.storageOption.external,
                    dbEnum.storageOption.ram,
                  ]
                    .map((s) => `"${s}"`)
                    .join(', ')}`
                )
    
            if (a.isSingleton) mask.push('singleton')
    
            if (a.isWritable) mask.push('writable')
    
            if (a.isNullable) mask.push('nullable')
    
            if (a.mustUseTimedWrite) mask.push('must_use_timed_write')
    
            let zap_type = 'UNKNOWN ATTRIBUTE TYPE'
    
            if (a.typeInfo.atomicType) {
    
              zap_type = a.typeInfo.atomicType
    
            } else if (a.typeInfo.type == dbEnum.zclType.struct) {
    
            } else if (a.typeInfo.type == dbEnum.zclType.enum && a.typeInfo.size) {
              zap_type = 'ENUM' + a.typeInfo.size * 8
    
            } else if (
              a.typeInfo.type == dbEnum.zclType.bitmap &&
              a.typeInfo.size
            ) {
              zap_type = 'BITMAP' + a.typeInfo.size * 8
    
              id: asMEI(a.manufacturerCode, a.code), // attribute code
    
              type: `ZAP_TYPE(${cHelper.asDelimitedMacro(zap_type)})`, // type
    
              mask: mask, // array of special properties
    
              defaultValue: attributeDefaultValue, // default value, pointer to default value, or pointer to min/max/value triplet.
    
              name: a.name,
              comment: cluster.comment,
    
    
            if (a.manufacturerCode) {
              let att = {
                index: attributeList.indexOf(attr),
                mfgCode: a.manufacturerCode,
              }
              attributeMfgCodes.push(att)
            }
    
    Timotej Ecimovic's avatar
    Timotej Ecimovic 已提交
          c.commands.sort(zclUtil.commandComparator)
    
            // ZAP files can have nonsense incoming/outgoing values,
            // unfortunately, claiming that client-sourced commands are
            // incoming for a client-side cluster.  Make sure that we only
            // set the flags that actually make sense based on the command
            // and cluster instance we are looking at.
            if (
              cmd.source == dbEnum.source.client &&
              c.side == dbEnum.side.server &&
    
            ) {
              mask.push('incoming_server')
            }
            if (
              cmd.source == dbEnum.source.client &&
              c.side == dbEnum.side.client &&
    
            ) {
              mask.push('outgoing_client')
            }
            if (
              cmd.source == dbEnum.source.server &&
              c.side == dbEnum.side.server &&
    
            ) {
              mask.push('outgoing_server')
            }
            if (
              cmd.source == dbEnum.source.server &&
              c.side == dbEnum.side.client &&
    
            ) {
              mask.push('incoming_client')
    
              endpointId: ept.endpointId,
    
              clusterId: asMEI(c.manufacturerCode, c.code),
              commandId: asMEI(cmd.manufacturerCode, cmd.code),
    
              name: cmd.name,
              comment: cluster.comment,
    
              responseName: cmd.responseName,
    
              responseId:
                cmd.responseRef !== null
                  ? asMEI(cmd.responseManufacturerCode, cmd.responseCode)
                  : null,
    
            cluster.commands.push(command)
    
    
            if (cmd.manufacturerCode) {
              let mfgCmd = {
                index: commandList.length - 1,
                mfgCode: cmd.manufacturerCode,
              }
              commandMfgCodes.push(mfgCmd)
            }
    
    
          // Go over the events
          c.events.sort(zclUtil.eventComparator)
    
          c.events.forEach((ev) => {
            let event = {
              eventId: asMEI(ev.manufacturerCode, ev.code),
              name: ev.name,
              comment: cluster.comment,
            }
            eventList.push(event)
          })
    
    
          endpointAttributeSize += clusterAttributeSize
          cluster.attributeSize = clusterAttributeSize
          clusterList.push(cluster)
    
    
          if (c.manufacturerCode) {
            let clt = {
    
              mfgCode: c.manufacturerCode,
            }
            clusterMfgCodes.push(clt)
          }
    
        endpoint.attributeSize = endpointAttributeSize
        endpointList.push(endpoint)
    
    Timotej Ecimovic's avatar
    Timotej Ecimovic 已提交
      return {
    
        endpointList: endpointList,
        clusterList: clusterList,