Source code for ansys.aedt.toolkits.common.backend.api

# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import base64
from dataclasses import dataclass
import gc
import os
import time
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Tuple

import ansys.aedt.core
from ansys.aedt.core import Desktop
from ansys.aedt.core.generic.general_methods import active_sessions

if ansys.aedt.core.__version__ <= "0.11.0":
    from ansys.aedt.core.misc import list_installed_ansysem

    list_installed_aedt = list_installed_ansysem()
else:
    from ansys.aedt.core.generic.aedt_versions import aedt_versions

    list_installed_aedt = aedt_versions.list_installed_ansysem


from pydantic import ValidationError

from ansys.aedt.toolkits.common.backend.constants import NAME_TO_AEDT_APP
from ansys.aedt.toolkits.common.backend.logger_handler import logger
from ansys.aedt.toolkits.common.backend.models import common_properties
from ansys.aedt.toolkits.common.backend.thread_manager import ThreadManager
from ansys.aedt.toolkits.common.utils import PropertiesUpdate
from ansys.aedt.toolkits.common.utils import ToolkitThreadStatus


@dataclass
class ToolkitConnectionStatus:
    """Provides an enumeration of statuses for a toolkit connection."""

    desktop: Optional[Desktop] = None

    def __str__(self):
        if self.desktop:
            res = f"Toolkit is connected to process {self.desktop.aedt_process_id}."
            if self.desktop.port != 0:
                res += f" on Grpc {self.desktop.port}."
        else:
            res = "Toolkit is not connected to AEDT."
        return res

    def is_connected(self):
        return self.desktop is not None


[docs] class Common: """Provides the API for controlling the toolkits. This class provides basic functions to control AEDT and EDB and the properties to share between the backend and UI. Parameters ---------- backend_properties : :class:`backend.models.Properties` Updated properties. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import Common >>> toolkit_api = Common() >>> toolkit_properties = toolkit_api.get_properties() >>> new_properties = {"aedt_version": "2024.2"} >>> toolkit_api.set_properties(new_properties) >>> new_properties = toolkit_api.get_properties() """ def __init__(self, backend_properties=None): if backend_properties: self.properties = backend_properties self.thread_manager = ThreadManager(self.properties) else: self.properties = common_properties self.thread_manager = ThreadManager() self.logger = logger
[docs] def get_properties(self) -> Dict[str, str]: """Get the toolkit properties. Returns ------- dict Dictionary containing the toolkit properties. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import Common >>> toolkit_api = Common() >>> toolkit_api.get_properties() {"property1": value1, "property2": value2} """ res = self.properties.model_dump() return res
[docs] def set_properties(self, data: Dict[str, Any]): """Assign the passed data to the internal data model. Parameters ---------- data : dict Dictionary containing the properties to update. Returns ------- tuple[bool, str] Tuple indicating the success status and a message. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import Common >>> toolkit_api = Common() >>> value2 = 2 >>> toolkit_api.set_properties({"property1": "value1", "property2": value2}) """ logger.info("Updating internal properties.") if not data: msg = PropertiesUpdate.EMPTY.value logger.debug(msg) return False, msg try: is_updated = self._update_properties(self.properties, data) msg = PropertiesUpdate.SUCCESS.value logger.debug(msg) return is_updated, msg except ValidationError: msg = PropertiesUpdate.VALIDATION_ERROR.value logger.error(msg) return False, msg
def _update_properties(self, properties: Any, data: Dict[str, Any]): """Helper function to update properties recursively.""" is_updated = False for key, value in data.items(): is_updated = False if hasattr(properties, key): logger.debug(f"Updating '{key}' with value {value}") setattr(properties, key, value) is_updated = True if not is_updated: if hasattr(properties, "model_fields"): for attr_name in properties.model_fields: attr = getattr(properties, attr_name) if hasattr(attr, "__dict__"): is_updated = self._update_properties(attr, data) if is_updated: break return is_updated
[docs] def launch_thread(self, process) -> ThreadManager: """Launch the thread.""" return self.thread_manager.launch_thread(process)
[docs] def get_thread_status(self) -> ToolkitThreadStatus: """Get the toolkit thread status. Returns ------- bool ``True`` when active, ``False`` when inactive. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import Common >>> toolkit_api = Common() >>> toolkit_api.get_thread_status() """ thread_running = self.thread_manager.is_toolkit_thread_running() is_toolkit_busy = self.properties.is_toolkit_busy if thread_running and is_toolkit_busy: # pragma: no cover res = ToolkitThreadStatus.BUSY logger.debug(res.value) elif (not thread_running and is_toolkit_busy) or (thread_running and not is_toolkit_busy): # pragma: no cover res = ToolkitThreadStatus.CRASHED logger.error(res.value) else: res = ToolkitThreadStatus.IDLE logger.debug(res.value) return res
[docs] @staticmethod def installed_aedt_version() -> List: """ Get the installed AEDT versions. Returns ------- list List of installed AEDT versions. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import Common >>> toolkit_api = Common() >>> toolkit_api.installed_aedt_version() ["2023.2", "2024.1", "2024.2"] """ # Detect existing AEDT installation installed_versions = [] for ver in list_installed_aedt: if "ANSYSEMSV_ROOT" in ver: # pragma: no cover # Handle the special case installed_versions.append( "20{}.{} STUDENT".format( ver.replace("ANSYSEMSV_ROOT", "")[:2], ver.replace("ANSYSEMSV_ROOT", "")[-1] ) ) else: installed_versions.append( "20{}.{}".format(ver.replace("ANSYSEM_ROOT", "")[:2], ver.replace("ANSYSEM_ROOT", "")[-1]) ) logger.debug(str(installed_versions)) return installed_versions
[docs] def aedt_sessions(self) -> Dict[int, int]: """Get information for the active AEDT sessions. Returns ------- dict Dictionary of AEDT process IDs (PIDS) {AEDT PID: port}. If the PID corresponds to a COM session, the port is set to ``-1``. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import Common >>> toolkit_api = Common() >>> toolkit_api.aedt_sessions() {pid1: grpc_port1, pid2: grpc_port2} """ res = {} if not self.properties.is_toolkit_busy and self.properties.aedt_version: res = active_sessions( version=self.properties.aedt_version, student_version=False, non_graphical=self.properties.non_graphical ) if res: logger.debug(f"Active AEDT sessions: {res}.") else: # pragma: no cover logger.debug("No active sessions.") return res
[docs] def wait_to_be_idle(self, timeout: int = 60) -> bool: """Wait for the thread to be idle and ready to accept a new task. Parameters ---------- timeout : int, optional Time out in seconds. The default is ``60``. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon >>> toolkit_api = AEDTCommon() >>> toolkit_api.launch_aedt() >>> toolkit_api.wait_to_be_idle() >>> toolkit_api.get_design_names() """ time.sleep(1) status = self.get_thread_status() cont = 0 while status == ToolkitThreadStatus.BUSY: # pragma: no cover time.sleep(1) cont += 1 status = self.get_thread_status() if cont == timeout: return False return True
[docs] @staticmethod def serialize_obj_base64(file_path): """Encode a bytes-like object. Parameters ---------- file_path : str Path to the file to serialize. Returns ------- bytes Encoded data. """ with open(file_path, "rb") as f: data = f.read() encoded_data = base64.b64encode(data) return encoded_data
[docs] class AEDTCommon(Common): """Provides common functions for controlling AEDT. This class provides basic functions for controlling AEDT and properties to share between the backend and UI. Parameters ---------- backend_properties : :class:`backend.models.Properties` Updated properties. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon >>> toolkit_api = AEDTCommon() >>> msg = toolkit_api.launch_aedt() """ def __init__(self, backend_properties: Optional[ThreadManager] = None): if backend_properties: self.properties = backend_properties self.thread_manager = ThreadManager(self.properties) else: self.properties = common_properties self.thread_manager = ThreadManager() super().__init__(self.properties) self.desktop = None self.aedtapp = None
[docs] def is_aedt_connected(self) -> Tuple[bool, str]: """Check if AEDT is connected. Returns ------- tuple[bool, str] Tuple indicating the connection status and a message. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon >>> toolkit_api = AEDTCommon() >>> toolkit_api.launch_aedt() >>> toolkit_api.wait_to_be_idle() >>> toolkit_api.connect_aedt() >>> toolkit_api.is_aedt_connected() (True, "toolkit connected to process <process_id> on Grpc <grpc_port>") >>> toolkit_api.release_aedt() """ tcs = ToolkitConnectionStatus(desktop=self.desktop) connected = tcs.is_connected() msg = str(tcs) logger.debug(msg) return connected, msg
[docs] def launch_aedt(self) -> bool: """Launch AEDT. This method is launched in a thread if gRPC is enabled. AEDT is released once it is opened. Returns ------- bool ``True`` when successful, ``False`` when failed. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon >>> toolkit_api = AEDTCommon() >>> toolkit_api.launch_aedt() """ # Check if the backend is already connected to an AEDT session connected, msg = self.is_aedt_connected() if not connected: logger.debug("Launching AEDT.") ansys.aedt.core.settings.use_grpc_api = self.properties.use_grpc ansys.aedt.core.settings.enable_logger = self.properties.debug version, is_student = self.__get_aedt_version() desktop_args = { "specified_version": version, "non_graphical": self.properties.non_graphical, "student_version": is_student, } # AEDT with COM if self.properties.selected_process == 0: desktop_args["new_desktop_session"] = True # AEDT with gRPC elif self.properties.use_grpc: # pragma: no cover desktop_args["new_desktop_session"] = False desktop_args["port"] = self.properties.selected_process else: # pragma: no cover desktop_args["new_desktop_session"] = False desktop_args["aedt_process_id"] = self.properties.selected_process self.desktop = ansys.aedt.core.Desktop(**desktop_args) if not self.desktop: # pragma: no cover logger.error("AEDT not launched.") return False logger.debug("AEDT launched.") # Save AEDT session properties if self.properties.use_grpc: self.properties.selected_process = self.desktop.port logger.debug("Grpc port {}.".format(str(self.desktop.port))) else: # pragma: no cover self.properties.selected_process = self.desktop.aedt_process_id logger.debug("Process ID {}.".format(str(self.desktop.aedt_process_id))) self.__save_project_info() if self.desktop.project_list(): # pragma: no cover # If there are projects not saved in the session, PyAEDT could find issues loading some properties self.desktop.save_project() self.release_aedt(False, False) logger.debug("AEDT is released and project properties are loaded.") return True
[docs] def connect_aedt(self) -> bool: """Connect to an existing AEDT session. Returns ------- bool ``True`` when successful, ``False`` when failed. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon >>> toolkit_api = AEDTCommon() >>> toolkit_api.launch_aedt() >>> toolkit_api.wait_to_be_idle() >>> toolkit_api.connect_aedt() >>> toolkit_api.release_aedt() """ if self.properties.selected_process == 0: # pragma: no cover logger.error("Process ID is not defined.") return False is_aedt_connected = self.is_aedt_connected() if is_aedt_connected[0]: logger.debug("Toolkit is connected to AEDT.") return True # Connect to AEDT ansys.aedt.core.settings.use_grpc_api = self.properties.use_grpc ansys.aedt.core.settings.enable_logger = self.properties.debug logger.debug("Connecting AEDT.") version, is_student = self.__get_aedt_version() desktop_args = { "specified_version": version, "non_graphical": self.properties.non_graphical, "student_version": is_student, "new_desktop_session": False, } if self.properties.use_grpc: desktop_args["port"] = self.properties.selected_process else: # pragma: no cover desktop_args["aedt_process_id"] = self.properties.selected_process gc.collect() self.desktop = ansys.aedt.core.Desktop(**desktop_args) if not self.desktop: # pragma: no cover logger.error("Toolkit is not connected to AEDT.") return False logger.debug("Toolkit is connected to AEDT.") return True
[docs] def connect_design(self, app_name: Optional[str] = None): """Connect to an application design. If a design exists, this method uses the active project and design. If a design does not exist, this method creates a design of the specified type. If no application is specified, the default is ``"HFSS"``. Parameters ---------- app_name : str AEDT application name. Options are: * ``"Circuit"`` * ``"EMIT"`` * ``"HFSS"`` * ``"HFSS3DLayout"`` * ``"Icepak"`` * ``"Maxwell2D"`` * ``"Maxwell3D"`` * ``"Q2D"`` * ``"Q3D"`` * ``"Rmxprt"`` * ``"TwinBuilder"`` * ``"Mechanical"`` Returns ------- bool Returns ``True`` if the connection to a design is successful, ``False`` otherwise. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon >>> toolkit_api = AEDTCommon() >>> toolkit_api.launch_aedt() >>> toolkit_api.wait_to_be_idle() >>> toolkit_api.connect_design() """ if self.aedtapp: self.release_aedt() if not self.connect_aedt(): # pragma: no cover return False project_name = self.properties.active_project design_name = "No Design" if self.properties.active_design: design_name = self.properties.active_design ansys.aedt.core.settings.use_grpc_api = self.properties.use_grpc ansys.aedt.core.settings.enable_logger = self.properties.debug if not app_name: app_name = "HFSS" # Select app aedt_app = ansys.aedt.core.Hfss if design_name != "No Design": project_name = self.get_project_name(project_name) active_design = design_name if design_name in self.properties.design_list[project_name]: self.aedtapp = self.desktop[[project_name, design_name]] if not self.aedtapp: # pragma: no cover self.release_aedt(False, False) logger.error("Wrong active project and design.") return False active_design = self.aedtapp.design_name elif app_name in list(NAME_TO_AEDT_APP.keys()): design_name = ansys.aedt.core.generate_unique_name(app_name) aedt_app = getattr(ansys.aedt.core, NAME_TO_AEDT_APP[app_name]) active_design = design_name else: logger.info("AEDT application is not available in PyAEDT. Creating HFSS design.") design_name = ansys.aedt.core.generate_unique_name("Hfss") active_design = design_name if not self.aedtapp and aedt_app: version, is_student = self.__get_aedt_version() aedt_app_args = { "specified_version": version, "port": self.properties.selected_process, "non_graphical": self.properties.non_graphical, "new_desktop_session": False, "projectname": project_name, "designname": active_design, "student_version": is_student, } if self.properties.use_grpc: aedt_app_args["port"] = self.properties.selected_process else: # pragma: no cover aedt_app_args["aedt_process_id"] = self.properties.selected_process self.aedtapp = aedt_app(**aedt_app_args) self.aedtapp.save_project() self.__save_project_info() if self.aedtapp: project_name = self.aedtapp.project_file if self.aedtapp.project_file not in self.properties.project_list: # pragma: no cover self.properties.project_list.append(project_name) self.properties.design_list[self.aedtapp.project_name] = [active_design] if ( self.aedtapp.design_list and active_design not in self.properties.design_list[self.aedtapp.project_name] ): # pragma: no cover self.properties.design_list[self.aedtapp.project_name].append(active_design) self.properties.active_project = project_name self.properties.active_design = active_design logger.info("Toolkit is connected to AEDT design.") return True else: # pragma: no cover logger.error("Toolkit is not connected to AEDT design.") return False
[docs] def release_aedt(self, close_projects=False, close_on_exit=False): """Release AEDT. Parameters ---------- close_projects : bool, optional Whether to close the AEDT projects that are open in the session. The default is ``True``. close_on_exit : bool, optional Whether to close the active AEDT session on exiting AEDT. The default is ``True``. Returns ------- bool ``True`` when successful, ``False`` when failed. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon >>> toolkit_api = AEDTCommon() >>> toolkit_api.launch_aedt() >>> toolkit_api.wait_to_be_idle() >>> toolkit_api.release_aedt(True, True) """ released = False if self.desktop: try: released = self.desktop.release_desktop(close_projects, close_on_exit) self.desktop = None self.aedtapp = None except: # pragma: no cover logger.error("AEDT is not released.") return False if self.aedtapp: # pragma: no cover try: released = self.aedtapp.release_desktop(close_projects, close_on_exit) self.aedtapp = None except: logger.error("AEDT is not released.") return False if not released and close_projects and close_on_exit and self.connect_aedt(): self.desktop.release_desktop(close_projects, close_on_exit) logger.info("AEDT is released.") gc.collect() return True
[docs] def open_project(self, project_name=None): """Open an AEDT project. Parameters ---------- project_name : str, optional Full path to the project. Returns ------- bool ``True`` when successful, ``False`` when failed. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon >>> toolkit_api = AEDTCommon() >>> toolkit_api.launch_aedt() >>> toolkit_api.wait_to_be_idle() >>> toolkit_api.open_project("path/to/file") >>> toolkit_api.release_aedt() """ if self.aedtapp: self.release_aedt() if not self.connect_aedt(): # pragma: no cover return False if ( not project_name and self.properties.active_project and os.path.exists(self.properties.active_project) ): # pragma: no cover project_name = os.path.abspath(self.properties.active_project) if not os.path.exists(project_name + ".lock") and self.desktop and project_name: self.desktop.odesktop.OpenProject(project_name) logger.debug("Project {} is opened".format(project_name)) self.__save_project_info() self.release_aedt(False, False) return True self.release_aedt(False, False) return False
[docs] def save_project(self, project_path=None, release_aedt=True): """Save the project. This method uses the properties to get the project path. This method is launched in a thread. Parameters ---------- project_path : str, optional Path of the AEDT project. The default value is ``None``, in which case the current file is overwritten. release_aedt : bool, optional Release PyAEDT object. The default value is ``True``. Returns ------- bool Returns ``True`` if the connection is successful, ``False`` otherwise. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon >>> toolkit_api = AEDTCommon() >>> toolkit_api.launch_aedt() >>> toolkit_api.wait_to_be_idle() >>> toolkit_api.connect_aedt() >>> toolkit_api.save_project() """ if self.connect_aedt(): if project_path and self.properties.active_project != project_path: old_project_name = self.get_project_name(self.properties.active_project) active_project_name = self.get_project_name(self.properties.active_project) self.desktop.save_project(project_path=os.path.abspath(project_path), project_name=active_project_name) index = self.properties.project_list.index(self.properties.active_project) self.properties.project_list.pop(index) self.properties.active_project = project_path self.properties.project_list.append(project_path) new_project_name = self.get_project_name(self.properties.active_project) self.properties.design_list[new_project_name] = self.properties.design_list[old_project_name] if old_project_name != new_project_name: del self.properties.design_list[old_project_name] else: self.desktop.save_project() self.__save_project_info() if release_aedt: self.release_aedt(False, False) logger.debug("Project is saved: {}".format(project_path)) return True else: # pragma: no cover logger.error("Project is not saved.") return False
[docs] @staticmethod def get_project_name(project_path) -> str: """Get the project name from the project path. Returns ------- str Project name. """ return os.path.splitext(os.path.basename(project_path))[0]
[docs] def get_design_names(self) -> List[str]: """Get the design names for a specific project. The first design name returned is the active design. Returns ------- list List of design names. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon >>> toolkit_api = AEDTCommon() >>> toolkit_api.launch_aedt() >>> toolkit_api.wait_to_be_idle() >>> toolkit_api.get_design_names() """ design_list: List[str] = [] if self.properties.selected_process == 0: # pragma: no cover logger.error("Process ID not defined") return design_list active_project = os.path.splitext(os.path.basename(self.properties.active_project))[0] if active_project and active_project != "No project": for design in self.properties.design_list[active_project]: design_list.append(design) if self.properties.active_design in design_list: index = design_list.index(self.properties.active_design) design_list.insert(0, design_list.pop(index)) else: design_list.append(self.properties.active_design) return design_list
[docs] def export_aedt_model( self, obj_list=None, export_path=None, export_as_single_objects=True, air_objects=False, encode=True ): """Export the model in the OBJ format and then encode the file if the ``encode`` parameter is enabled. Parameters ---------- obj_list : list, optional List of objects to export. The default is ``None``, in which case every model object except 3D, vacuum, and air objects are exported. export_path : str, optional Full path of the exported OBJ file. The default is ``None``, in which case the file is exported in the working directory. export_as_single_objects : bool, optional Whether to export the model as a single object. The default is ``True``. If ``False``, the model is exported as a list of objects for each object. air_objects : bool, optional Whether to export air and vacuum objects. The default is ``False``. encode : bool, optional Whether to encode the file. The default is ``True``. Returns ------- list or dict List of exported OBJ files or encoded data. """ if not self.aedtapp: self.connect_design() files = [] if self.aedtapp: self.aedtapp.save_project() files = self.aedtapp.post.export_model_obj( assignment=obj_list, export_path=export_path, export_as_single_objects=export_as_single_objects, air_objects=air_objects, ) self.release_aedt(False, False) # Plot exported files using the following code # from ansys.aedt.core.generic.plot import ModelPlotter # model = ModelPlotter() # for file in files: # model.add_object(file[0], file[1], file[2]) if encode: model_info = {} for element in files: element_path = element[0] encoded_obj = self.serialize_obj_base64(element_path) name = os.path.splitext(os.path.basename(element_path))[0] model_info[name] = [encoded_obj.decode("utf-8"), element[1], element[2]] return model_info return files
def __get_aedt_version(self): """Get AEDT version and if the student version is used.""" if "STUDENT" in self.properties.aedt_version: # pragma: no cover version_text = self.properties.aedt_version.split(" ") version = version_text[0] is_student = True else: version = self.properties.aedt_version is_student = False return version, is_student def __save_project_info(self): """Save the project and design information.""" # Save project and design info new_properties = {} project_list = self.desktop.odesktop.GetProjectList() if project_list: new_properties["project_list"] = [] active_project = self.desktop.odesktop.GetActiveProject() if not active_project: # pragma: no cover active_project = self.desktop.odesktop.SetActiveProject(project_list[0]) active_project_name = active_project.GetName() active_design = active_project.GetActiveDesign() # Save active design info if active_design: active_design_name = active_design.GetName() active_design_name = ( active_design_name if ";" not in active_design_name else active_design_name.split(";")[1] ) new_properties["active_design"] = active_design_name elif active_project.GetChildNames(): # pragma: no cover # This case covers when the project has designs but none of them are active active_design_name = active_project.GetChildNames()[0] active_project.SetActiveDesign(active_design_name) new_properties["active_design"] = active_design_name # Save active project active_project_path = active_project.GetPath() new_properties["active_project"] = os.path.join(active_project_path, active_project_name + ".aedt") # Save projects info new_properties["design_list"] = {} for project in project_list: oproject = self.desktop.odesktop.SetActiveProject(project) project_name = oproject.GetName() project_path = oproject.GetPath() logger.debug("Project name: {}".format(project_name)) new_properties["project_list"].append(os.path.join(project_path, project_name + ".aedt")) new_properties["design_list"][project_name] = [] design_list = oproject.GetChildNames() if design_list: for design_name in design_list: new_properties["design_list"][project_name].append(design_name) if new_properties: self.set_properties(new_properties)
[docs] class EDBCommon(Common): """Provides the generic API for controlling EDB. This class provides basic functions to control EDB and properties to share between the backend and UI. Parameters ---------- backend_properties : :class:`backend.models.Properties` Updated properties. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import EDBCommon >>> toolkit_api = EDBCommon() >>> toolkit_api.load_edb("path/to/file") """ def __init__(self, backend_properties=None): self.properties = common_properties if backend_properties: self.properties = backend_properties super().__init__(self.properties) self.edb = None
[docs] def load_edb(self, edb_path=None): """Load the EDB project. Parameters ---------- edb_path : str, optional Full path to the ``aedb`` folder. Returns ------- bool ``True`` when successful, ``False`` when failed. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import EDBCommon >>> toolkit_api = EDBCommon() >>> toolkit_api.load_edb("path/to/file") >>> toolkit_api.close_edb() """ if not edb_path: # pragma: no cover edb_path = self.properties.active_project if self.edb: logger.error(f"Close EDB {edb_path} before loading a new project.") return False if os.path.exists(edb_path): aedt_version = self.properties.aedt_version ansys.aedt.core.settings.enable_logger = self.properties.debug ansys.aedt.core.settings.enable_debug_edb_logger = self.properties.debug self.properties.active_project = edb_path self.edb = ansys.aedt.core.Edb(edbversion=aedt_version, edbpath=edb_path) logger.debug("Project {} is opened".format(edb_path)) return True else: logger.error("Project {} does not exist".format(edb_path)) return False
[docs] def close_edb(self): """Close the EDB project. Returns ------- bool ``True`` when successful, ``False`` when failed. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import EDBCommon >>> toolkit_api = EDBCommon() >>> toolkit_api.load_edb("path/to/file") >>> toolkit_api.close_edb() """ if self.edb: self.edb.close_edb() self.edb = None logger.info("EDB is closed.") return True else: logger.error("EDB is not initialized.") return False
[docs] def save_edb(self, edb_path=None): """Save the EDB project. Parameters ---------- edb_path : str, optional Full path to the ``aedb`` folder. The default is ``None``. Returns ------- bool ``True`` when successful, ``False`` when failed. Examples -------- >>> from ansys.aedt.toolkits.common.backend.api import EDBCommon >>> toolkit_api = EDBCommon() >>> toolkit_api.load_edb("path/to/file") >>> toolkit_api.save_edb("path/to/new_file") >>> toolkit_api.close_edb() """ if self.edb: if not edb_path or os.path.normpath(edb_path) == os.path.normpath(self.edb.edbpath): self.edb.save() edb_path = self.edb.edbpath else: self.edb.save_as(edb_path) logger.info("Project {} saved".format(edb_path)) return True else: # pragma: no cover return False