| #!/usr/bin/env python |
| # Copyright (c) 2022 Project CHIP Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import os |
| import re |
| from typing import Tuple, Union |
| |
| from matter_idl.generators import CodeGenerator, GeneratorStorage |
| from matter_idl.generators.types import (BasicInteger, BasicString, FundamentalType, IdlBitmapType, IdlEnumType, IdlType, |
| ParseDataType, TypeLookupContext) |
| from matter_idl.matter_idl_types import Attribute, Cluster, ClusterSide, Field, Idl |
| |
| |
| def camel_to_const(s): |
| return re.sub("([a-z])([A-Z])", lambda y: y.group(1) + "_" + y.group(2), s).upper() |
| |
| |
| def create_lookup_context(idl: Idl, cluster: Cluster) -> TypeLookupContext: |
| """ |
| A filter to mark a lookup context to be within a specific cluster. |
| |
| This is used to specify how structure/enum/other names are looked up. |
| Generally one looks up within the specific cluster then if cluster does |
| not contain a definition, we loop at global namespacing. |
| """ |
| return TypeLookupContext(idl, cluster) |
| |
| |
| def get_field_info(definition: Field, cluster: Cluster, idl: Idl) -> Tuple[str, str, Union[str, int, None], str]: |
| context = create_lookup_context(idl, cluster) |
| actual = ParseDataType(definition.data_type, context) |
| |
| if isinstance(actual, (IdlEnumType, IdlBitmapType)): |
| actual = actual.base_type |
| |
| if isinstance(actual, BasicString): |
| return 'OctetString', 'char', actual.max_length, \ |
| 'ZCL_%s_ATTRIBUTE_TYPE' % actual.idl_name.upper() |
| |
| if isinstance(actual, BasicInteger): |
| name = actual.idl_name.upper() |
| ty = "int%d_t" % actual.power_of_two_bits |
| if not actual.is_signed: |
| ty = "u" + ty |
| return "", ty, actual.byte_count, "ZCL_%s_ATTRIBUTE_TYPE" % name |
| if isinstance(actual, FundamentalType): |
| if actual == FundamentalType.BOOL: |
| return "", "bool", 1, "ZCL_BOOLEAN_ATTRIBUTE_TYPE" |
| elif actual == FundamentalType.FLOAT: |
| return "", "float", 4, "ZCL_SINGLE_ATTRIBUTE_TYPE" |
| elif actual == FundamentalType.DOUBLE: |
| return "", "double", 8, "ZCL_DOUBLE_ATTRIBUTE_TYPE" |
| else: |
| raise Exception('Unknown fundamental type: %r' % actual) |
| if isinstance(actual, IdlType): |
| return '', actual.idl_name, 'sizeof(%s)' % actual.idl_name, \ |
| 'ZCL_STRUCT_ATTRIBUTE_TYPE' |
| raise Exception('UNKNOWN TYPE: %s' % actual) |
| |
| |
| def get_raw_size_and_type(attr: Attribute, cluster: Cluster, idl: Idl) -> str: |
| container, cType, size, matterType = get_field_info( |
| attr.definition, cluster, idl) |
| if attr.definition.is_list: |
| return 'ZCL_ARRAY_ATTRIBUTE_TYPE, {}'.format(size) |
| return '{}, {}'.format(matterType, size) |
| |
| |
| def get_field_type(definition: Field, cluster: Cluster, idl: Idl): |
| container, cType, size, matterType = get_field_info( |
| definition, cluster, idl) |
| if container == 'OctetString': |
| return 'std::string' |
| if definition.is_list: |
| cType = 'std::vector<{}>'.format(cType) |
| if definition.is_nullable: |
| cType = '::chip::app::DataModel::Nullable<{}>'.format(cType) |
| if definition.is_optional: |
| cType = '::chip::Optional<{}>'.format(cType) |
| return cType |
| |
| |
| def get_attr_type(attr: Attribute, cluster: Cluster, idl: Idl): |
| return get_field_type(attr.definition, cluster, idl) |
| |
| |
| def get_attr_init(attr: Attribute, cluster: Cluster, idl: Idl): |
| if attr.definition.name == 'clusterRevision': |
| return ', ZCL_' + camel_to_const(cluster.name) + '_CLUSTER_REVISION' |
| return '' |
| |
| |
| def get_attr_mask(attr: Attribute, cluster: Cluster, idl: Idl): |
| masks = [] |
| if attr.is_writable: |
| masks.append('ATTRIBUTE_MASK_WRITABLE') |
| if masks: |
| return ' | '.join(masks) |
| return '0' |
| |
| |
| def get_dynamic_endpoint(idl: Idl): |
| for ep in idl.endpoints: |
| if ep.number == 2: |
| return ep |
| |
| |
| def is_dynamic_cluster(cluster: Cluster, idl: Idl): |
| ep = get_dynamic_endpoint(idl) |
| if not ep: |
| return True |
| for c in ep.server_clusters: |
| if cluster.name == c.name: |
| return True |
| return False |
| |
| |
| class BridgeGenerator(CodeGenerator): |
| """ |
| Generation of bridge cpp code for matter. |
| """ |
| |
| def __init__(self, storage: GeneratorStorage, idl: Idl, **kargs): |
| """ |
| Inintialization is specific for cpp generation and will add |
| filters as required by the cpp .jinja templates to function. |
| """ |
| super().__init__(storage, idl, fs_loader_searchpath=os.path.dirname(__file__)) |
| |
| self.jinja_env.filters['getType'] = get_attr_type |
| self.jinja_env.filters['getRawSizeAndType'] = get_raw_size_and_type |
| self.jinja_env.filters['getField'] = get_field_type |
| self.jinja_env.filters['getMask'] = get_attr_mask |
| self.jinja_env.filters['getInit'] = get_attr_init |
| self.jinja_env.filters['dynamicCluster'] = is_dynamic_cluster |
| # constcase will transform ID to I_D which is not what we want |
| # instead make the requirement a transition from lower to upper |
| self.jinja_env.filters['cameltoconst'] = camel_to_const |
| |
| def internal_render_all(self): |
| """ |
| Renders C++ |
| """ |
| for cluster in self.idl.clusters: |
| if not is_dynamic_cluster(cluster, self.idl): |
| continue |
| |
| if cluster.side != ClusterSide.SERVER: |
| output_file_name = "bridge/%sServer.h" % cluster.name |
| else: |
| output_file_name = "bridge/%s.h" % cluster.name |
| |
| self.internal_render_one_output( |
| template_path="BridgeClustersCpp.jinja", |
| output_file_name=output_file_name, |
| vars={ |
| 'cluster': cluster, |
| 'idl': self.idl, |
| } |
| ) |
| |
| self.internal_render_one_output( |
| template_path="BridgeClustersCommon.jinja", |
| output_file_name="bridge/BridgeClustersImpl.h", |
| vars={ |
| 'clusters': self.idl.clusters, |
| 'idl': self.idl, |
| } |
| ) |
| |
| self.internal_render_one_output( |
| template_path="BridgeClustersGlobalStructs.jinja", |
| output_file_name="bridge/BridgeGlobalStructs.h", |
| vars={ |
| 'idl': self.idl, |
| 'structs': self.idl.structs, |
| } |
| ) |