Exemplo n.º 1
0
class LayoutExporter(object):
    def __init__(self, external_packages=None):

        self.external_package_paths = make_paths_safe_for_omc(
            external_packages)

        self.logger = logging.getLogger('py_modelica_exporter.LayoutExporter')
        self.logger.setLevel(logging.NOTSET)
        self.logger.info(
            'Initializing LayoutExporter({0})'.format(external_packages))

        # start om session
        self.omc = OMCSession()

        # load all packages
        self.load_packages(self.external_package_paths)

    def load_packages(self, external_package_paths):

        self.omc.loadModel('Modelica')

        for package_path in external_package_paths:
            if os.path.isfile(package_path):  # make sure the file exists
                if self.omc.loadFile(
                        package_path):  # try to load the package file
                    self.logger.info(
                        'Library loaded from : {0}'.format(package_path))
                else:
                    self.logger.warning(
                        'Failed to load: {0}'.format(package_path))
            else:
                self.logger.warning(
                    'File does not exist! Failed to load: {0}'.format(
                        package_path))

    def get_nth_comp_location(self, modelica_uri, n):

        x_origin = 0
        y_origin = 0
        x_extent = 0
        y_extent = 0
        rotation = 0
        flip_x = False
        flip_y = False

        # get the annotation info for Nth component (index begins at 1)
        try:
            nth_component_annotation = self.omc.getNthComponentAnnotation(
                modelica_uri, n)

            x_1 = nth_component_annotation[3]
            x_2 = nth_component_annotation[5]

            if x_1 > x_2:
                flip_x = True

            y_1 = nth_component_annotation[4]
            y_2 = nth_component_annotation[6]

            if y_1 > y_2:
                flip_y = True

            x_extent = abs(x_2 - x_1)
            y_extent = abs(y_2 - y_1)

            x_origin = nth_component_annotation[1]
            y_origin = nth_component_annotation[2]

            if x_origin == 0:
                x_origin = (x_1 + x_2) / 2
            if y_origin == 0:
                y_origin = (y_1 + y_2) / 2

            rotation = nth_component_annotation[7]

        except:
            self.logger.warning(
                'Could not get annotation for Nth component of {0}, N = {1}'.
                format(modelica_uri, n))

        return x_origin, y_origin, x_extent, y_extent, rotation, flip_x, flip_y

    def extract_assembly_layout(self, modelica_uri, assembly=None):

        if not assembly:
            assembly = ComponentAssembly()
            assembly.full_name = modelica_uri
            assembly.name = modelica_uri.split('.')[-1]

        try:
            mo_components = self.omc.getComponents(modelica_uri)

        except ValueError as value_error_exception:
            if value_error_exception.args[
                    0] == 'Could not parse OMC response.':
                self.logger.warning(
                    'Could not parse OMC response for getComponents({0})'.
                    format(modelica_uri))
                raise ParsingException

        for n in range(1, len(mo_components) + 1):

            nth_component_info = self.omc.getNthComponent(modelica_uri, n)

            x_origin, y_origin, x_extent, y_extent, rotation, flip_x, flip_y = self.get_nth_comp_location(
                modelica_uri, n)

            if x_origin - 0.5 * x_extent < assembly.extent['x_min']:
                assembly.extent['x_min'] = x_origin - 0.5 * x_extent
            if x_origin + 0.5 * x_extent > assembly.extent['x_max']:
                assembly.extent['x_max'] = x_origin + 0.5 * x_extent
            if y_origin - 0.5 * y_extent < assembly.extent['y_min']:
                assembly.extent['y_min'] = y_origin - 0.5 * y_extent
            if y_origin + 0.5 * y_extent > assembly.extent['y_max']:
                assembly.extent['y_max'] = y_origin + 0.5 * y_extent

            mo_type = nth_component_info[0]

            if self.omc.isConnector(mo_type):
                #connector = Connector()
                connector = ComponentShell()
                connector.full_name = mo_type
                connector.name = nth_component_info[1]
                connector.description = nth_component_info[2]

                #connector.relative_position['x'] = x_origin
                #connector.relative_position['y'] = y_origin
                connector.position['x'] = x_origin
                connector.position['y'] = y_origin
                connector.size['width'] = x_extent
                connector.size['height'] = y_extent

                #assembly.connectors.append(connector)
                assembly.component_shells[connector.name] = connector

            # We should only get 'internal' models within assemblies
            if self.omc.isModel(mo_type) or self.omc.isBlock(mo_type):
                comp_shell = ComponentShell()
                comp_shell.full_name = mo_type
                comp_shell.name = nth_component_info[1]
                comp_shell.description = nth_component_info[2]

                comp_shell.position['x'] = x_origin
                comp_shell.position['y'] = y_origin
                comp_shell.size['width'] = x_extent
                comp_shell.size['height'] = y_extent
                comp_shell.rotation = rotation
                comp_shell.flip_x = flip_x
                comp_shell.flip_y = flip_y

                comp_shell = self.extract_comp_shell_layout(
                    mo_type, comp_shell)

                assembly.component_shells[comp_shell.name] = comp_shell

        # Get internal connections
        for n in range(1, self.omc.getConnectionCount(modelica_uri) + 1):
            src, dst, v3 = self.omc.getNthConnection(modelica_uri, n)

            src_name, src_parent = self._get_connector_and_parent(src)
            dst_name, dst_parent = self._get_connector_and_parent(dst)

            connection = Connection()

            if src_name:
                connection.src_name = src_name
            if src_parent:
                connection.src_parent = src_parent
            if dst_name:
                connection.dst_name = dst_name
            if dst_parent:
                connection.dst_parent = dst_parent

            connection.path_points = self.omc.getNthConnectionPathPoints(
                modelica_uri, n)

            assembly.connections.append(connection)

        # Get inherited members
        mo_inheritance_count = self.omc.getInheritanceCount(modelica_uri)

        for i in range(1, mo_inheritance_count + 1):
            mo_extend_class_name = self.omc.getNthInheritedClass(
                modelica_uri, i)
            assembly = self.extract_assembly_layout(mo_extend_class_name,
                                                    assembly)

        return assembly

    def extract_comp_shell_layout(self, modelica_uri, comp_shell):

        try:
            mo_components = self.omc.getComponents(modelica_uri)

        except ValueError as value_error_exception:
            if value_error_exception.args[
                    0] == 'Could not parse OMC response.':
                self.logger.warning(
                    'Could not parse OMC response for getComponents({0})'.
                    format(modelica_uri))
                raise ParsingException

        for n in range(1, len(mo_components) + 1):

            nth_component_info = self.omc.getNthComponent(modelica_uri, n)

            x_origin, y_origin, x_extent, y_extent, rotation, flip_x, flip_y = self.get_nth_comp_location(
                modelica_uri, n)

            mo_type = nth_component_info[0]

            if self.omc.isConnector(mo_type):
                connector = Connector()
                connector.full_name = mo_type
                connector.name = nth_component_info[1]
                connector.description = nth_component_info[2]

                connector.relative_position['x'] = x_origin
                connector.relative_position['y'] = y_origin
                connector.size['width'] = x_extent
                connector.size['height'] = y_extent

                comp_shell.connectors.append(connector)

        # Get inherited members
        mo_inheritance_count = self.omc.getInheritanceCount(modelica_uri)

        for i in range(1, mo_inheritance_count + 1):
            mo_extend_class_name = self.omc.getNthInheritedClass(
                modelica_uri, i)
            comp_shell = self.extract_comp_shell_layout(
                mo_extend_class_name, comp_shell)

        return comp_shell

    def scale_layout_for_webgme(self, assembly_layout, scale=5):

        if isinstance(assembly_layout, ComponentAssembly):
            x_offset = abs(assembly_layout.extent['x_min']) + 10
            y_offset = abs(assembly_layout.extent['y_max']) + 10

            for name, comp_shell in assembly_layout.component_shells.iteritems(
            ):
                left_edge = comp_shell.position[
                    'x'] - 0.5 * comp_shell.size['width']
                top_edge = comp_shell.position[
                    'y'] + 0.5 * comp_shell.size['height']

                comp_shell.position['x'] = scale * (left_edge + x_offset)
                comp_shell.position['y'] = scale * (y_offset - top_edge)
                comp_shell.size['width'] *= scale
                comp_shell.size['height'] *= scale

                for connector in comp_shell.connectors:
                    rel_x = connector.relative_position['x']
                    rel_y = connector.relative_position['y']

                    # handle rotations, so port relative positions are correct
                    if comp_shell.rotation != 0:
                        import math

                        cos_theta = math.cos(comp_shell.rotation * math.pi /
                                             180)
                        sin_theta = math.sin(comp_shell.rotation * math.pi /
                                             180)

                        x_theta = rel_x * cos_theta - rel_y * sin_theta
                        y_theta = rel_x * sin_theta + rel_y * cos_theta

                        rel_x = x_theta
                        rel_y = y_theta

                    if comp_shell.flip_x:
                        rel_x = -rel_x

                    if comp_shell.flip_y:
                        rel_y = -rel_y

                    connector.relative_position['x'] = (
                        (rel_x + 100) / 200) * comp_shell.size['width']
                    connector.relative_position['y'] = (
                        1 - (rel_y + 100) / 200) * comp_shell.size['height']

            for connector in assembly_layout.connectors:
                connector.relative_position['x'] = scale * (
                    connector.relative_position['x'] + x_offset)
                connector.relative_position['y'] = scale * (
                    -connector.relative_position['y'] + y_offset)

            for connection in assembly_layout.connections:
                for pp in connection.path_points:
                    pp['x'] = scale * (pp['x'] + x_offset)
                    pp['y'] = scale * (-pp['y'] + y_offset)

                    # first_path_point = connection.path_points[0]
                    # last_path_point = connection.path_points[-1]
                    #
                    # connection.path_points = list()
                    # connection.path_points.append(first_path_point)
                    # connection.path_points.append(last_path_point)

    def test_omc_get_components(self, modelica_uri):

        with open('omcTest.txt', 'w') as f_out:
            components = self.omc.getComponents(modelica_uri)
            n = 0

            for tu in components:
                msg = '{0}: {1} (index in getComponents)\r'.format(
                    components.index(tu), tu)
                f_out.write(msg)

                n = n + 1
                nthComponentInfo = self.omc.getNthComponent(modelica_uri, n)
                msg = '{0}: {1} (getNthComponent)\r'.format(
                    n, nthComponentInfo)
                f_out.write(msg)

    def _get_connector_and_parent(self, connected):
        pieces = connected.split('.')
        parent = ""
        if len(pieces) == 2:
            parent = pieces[0]
            connector = pieces[1]
        else:
            connector = connected

        return connector, parent