diff --git a/_unittest/test_14_AedtLogger.py b/_unittest/test_14_AedtLogger.py index ba8900b741a..49bcd6a81a4 100644 --- a/_unittest/test_14_AedtLogger.py +++ b/_unittest/test_14_AedtLogger.py @@ -242,7 +242,7 @@ def test_05_disable_stdout(self): handler.close() logger._global.removeHandler(handler) assert stream_content[0] == "PyAEDT INFO: Info for Global\n" - assert stream_content[1] == "PyAEDT INFO: StdOut is enabled\n" + assert stream_content[1] == "PyAEDT INFO: Log on console is enabled.\n" assert stream_content[2] == "PyAEDT INFO: Info after re-enabling the stdout handler.\n" diff --git a/_unittest/test_20_HFSS.py b/_unittest/test_20_HFSS.py index 53bade2fc36..453bdafa39a 100644 --- a/_unittest/test_20_HFSS.py +++ b/_unittest/test_20_HFSS.py @@ -889,32 +889,30 @@ def test_30_assign_initial_mesh(self): assert self.aedtapp.mesh.assign_initial_mesh_from_slider(6) def test_30a_add_mesh_link(self): + design_name = self.aedtapp.design_name + nominal_adaptive = self.aedtapp.nominal_adaptive self.aedtapp.duplicate_design(self.aedtapp.design_name) - self.aedtapp.set_active_design(self.aedtapp.design_list[0]) - assert self.aedtapp.setups[0].add_mesh_link(design=self.aedtapp.design_list[1]) + self.aedtapp._setups = None + assert self.aedtapp.setups[0].add_mesh_link(design=design_name) meshlink_props = self.aedtapp.setups[0].props["MeshLink"] assert meshlink_props["Project"] == "This Project*" assert meshlink_props["PathRelativeTo"] == "TargetProject" - assert meshlink_props["Design"] == self.aedtapp.design_list[1] - assert meshlink_props["Soln"] == "MySetup : LastAdaptive" + assert meshlink_props["Design"] == design_name + assert meshlink_props["Soln"] == nominal_adaptive assert sorted(list(meshlink_props["Params"].keys())) == sorted(self.aedtapp.available_variations.variables) assert sorted(list(meshlink_props["Params"].values())) == sorted(self.aedtapp.available_variations.variables) assert not self.aedtapp.setups[0].add_mesh_link(design="") + assert self.aedtapp.setups[0].add_mesh_link(design=design_name, solution="MySetup : LastAdaptive") + assert not self.aedtapp.setups[0].add_mesh_link(design=design_name, solution="Setup_Test : LastAdaptive") assert self.aedtapp.setups[0].add_mesh_link( - design=self.aedtapp.design_list[1], solution="MySetup : LastAdaptive" - ) - assert not self.aedtapp.setups[0].add_mesh_link( - design=self.aedtapp.design_list[1], solution="Setup_Test : LastAdaptive" - ) - assert self.aedtapp.setups[0].add_mesh_link( - design=self.aedtapp.design_list[1], parameters=self.aedtapp.available_variations.nominal_w_values_dict + design=design_name, parameters=self.aedtapp.available_variations.nominal_w_values_dict ) example_project = os.path.join( local_path, "../_unittest/example_models", test_subfolder, diff_proj_name + ".aedt" ) example_project_copy = os.path.join(self.local_scratch.path, diff_proj_name + "_copy.aedt") shutil.copyfile(example_project, example_project_copy) - assert self.aedtapp.setups[0].add_mesh_link(design=self.aedtapp.design_list[1], project=example_project_copy) + assert self.aedtapp.setups[0].add_mesh_link(design=design_name, project=example_project_copy) def test_31_create_microstrip_port(self): self.aedtapp.insert_design("Microstrip") diff --git a/_unittest/test_30_Q2D.py b/_unittest/test_30_Q2D.py index 53a4e41c8f9..cb796e001cc 100644 --- a/_unittest/test_30_Q2D.py +++ b/_unittest/test_30_Q2D.py @@ -23,6 +23,7 @@ # SOFTWARE. import os +import time from _unittest.conftest import config from _unittest.conftest import desktop_version @@ -162,7 +163,9 @@ def test_14_export_matrix_data(self, add_app): q2d.matrices[2].name == "Test2" q2d.insert_reduced_matrix(q2d.MATRIXOPERATIONS.SetReferenceGround, "Circle2", "Test3") q2d.matrices[3].name == "Test3" - q2d.analyze_setup(q2d.active_setup) + q2d.analyze_setup(q2d.active_setup, blocking=False) + while q2d.desktop_class.are_there_simulations_running: + time.sleep(1) q2d.export_matrix_data(os.path.join(self.local_scratch.path, "test_2d.txt")) assert q2d.export_matrix_data(os.path.join(self.local_scratch.path, "test_2d.txt"), problem_type="CG") assert q2d.export_matrix_data( @@ -200,8 +203,6 @@ def test_14_export_matrix_data(self, add_app): os.path.join(self.local_scratch.path, "test_2d.txt"), precision=16, field_width=22 ) assert not q2d.export_matrix_data(os.path.join(self.local_scratch.path, "test_2d.txt"), precision=16.2) - assert q2d.export_matrix_data(os.path.join(self.local_scratch.path, "test_2d.txt"), freq="1", freq_unit="GHz") - assert q2d.export_matrix_data(os.path.join(self.local_scratch.path, "test_2d.txt"), freq="3", freq_unit="GHz") assert q2d.export_matrix_data(os.path.join(self.local_scratch.path, "test_2d.txt"), freq="3", freq_unit="Hz") assert q2d.export_matrix_data(os.path.join(self.local_scratch.path, "test_2d.txt"), use_sci_notation=True) assert q2d.export_matrix_data(os.path.join(self.local_scratch.path, "test_2d.txt"), use_sci_notation=False) diff --git a/pyaedt/aedt_logger.py b/pyaedt/aedt_logger.py index fce4e646541..6c3654576f5 100644 --- a/pyaedt/aedt_logger.py +++ b/pyaedt/aedt_logger.py @@ -800,18 +800,18 @@ def add_logger(self, destination, level=logging.DEBUG): def disable_desktop_log(self): """Disable the log in AEDT.""" self._log_on_desktop = False - self.info("Log on Desktop Message Manager is disabled") + self.info("Log on AEDT is disabled.") def enable_desktop_log(self): """Enable the log in AEDT.""" self._log_on_desktop = True - self.info("Log on Desktop Message Manager is enabled") + self.info("Log on AEDT is enabled.") def disable_stdout_log(self): """Disable printing log messages to stdout.""" self._log_on_screen = False self._global.removeHandler(self._std_out_handler) - self.info("StdOut is disabled") + self.info("Log on console is disabled.") def enable_stdout_log(self): """Enable printing log messages to stdout.""" @@ -824,7 +824,7 @@ def enable_stdout_log(self): self._std_out_handler.setFormatter(_logger_stdout_formatter) self._global.addHandler(self._std_out_handler) self._global.addHandler(self._std_out_handler) - self.info("StdOut is enabled") + self.info("Log on console is enabled.") def disable_log_on_file(self): """Disable writing log messages to an output file.""" @@ -843,14 +843,15 @@ def disable_log_on_file(self): if isinstance(_file_handler, FileHandler): _file_handler.close() self.project_logger.removeHandler(_file_handler) - self.info("Log on file is disabled") + self.info("Log on file is disabled.") def enable_log_on_file(self): """Enable writing log messages to an output file.""" self._log_on_file = True for _file_handler in self._files_handlers: self._global.addHandler(_file_handler) - self.info("Log on file is enabled") + if "baseFilename" in dir(_file_handler): + self.info("Log on file {} is enabled.".format(_file_handler.baseFilename)) def info(self, msg, *args, **kwargs): """Write an info message to the global logger.""" diff --git a/pyaedt/application/Design.py b/pyaedt/application/Design.py index 929e54e4b88..cc75422379b 100644 --- a/pyaedt/application/Design.py +++ b/pyaedt/application/Design.py @@ -502,7 +502,7 @@ def odesktop(self): >>> hfss.odesktop """ - return self._desktop + return self.desktop_class.odesktop @pyaedt_function_handler() def __delitem__(self, key): diff --git a/pyaedt/desktop.py b/pyaedt/desktop.py index 15592a04dcd..fc8215d2b21 100644 --- a/pyaedt/desktop.py +++ b/pyaedt/desktop.py @@ -209,7 +209,9 @@ def launch_aedt_in_lsf(non_graphical, port): # pragma: no cover def _check_grpc_port(port, machine_name=""): - s = socket.socket() + if not port: + return False + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: if not machine_name: machine_name = "127.0.0.1" @@ -226,10 +228,16 @@ def _check_grpc_port(port, machine_name=""): def _find_free_port(): from contextlib import closing - with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: - s.bind(("127.0.0.1", 0)) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - return s.getsockname()[1] + def _find(): + with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: + s.bind(("127.0.0.1", 0)) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + return s.getsockname()[1] + + while True: + new_port = _find() + if new_port not in list(active_sessions().values()): + return new_port def exception_to_desktop(ex_value, tb_data): # pragma: no cover @@ -476,7 +484,7 @@ def __new__(cls, *args, **kwargs): kwargs.get("specified_version") or kwargs.get("version") or None if (not args or len(args) < 1) else args[0] ) new_desktop = ( - kwargs.get("new_desktop_session") or kwargs.get("new_desktop") or False + kwargs.get("new_desktop_session") or kwargs.get("new_desktop") or True if (not args or len(args) < 3) else args[2] ) @@ -485,6 +493,9 @@ def __new__(cls, *args, **kwargs): specified_version = get_string_version(specified_version) port = kwargs.get("port") or 0 if (not args or len(args) < 7) else args[6] aedt_process_id = kwargs.get("aedt_process_id") or None if (not args or len(args) < 8) else args[7] + if not settings.remote_api: + pyaedt_logger.info("Python version %s", sys.version) + pyaedt_logger.info("PyAEDT version {}.".format(pyaedt_version)) if settings.use_multi_desktop and not inside_desktop and new_desktop: pyaedt_logger.info("Initializing new Desktop session.") return object.__new__(cls) @@ -659,10 +670,10 @@ def __init__( else: settings.aedt_version = version_key if starting_mode == "ironpython": # pragma no cover - self._logger.info("Launching PyAEDT outside AEDT with IronPython.") + self._logger.info("Launching PyAEDT with IronPython.") self._init_ironpython(non_graphical, new_desktop, version) elif starting_mode == "com": # pragma no cover - self._logger.info("Launching PyAEDT outside AEDT with CPython and PythonNET.") + self._logger.info("Launching PyAEDT with CPython and PythonNET.") self._init_dotnet( non_graphical, new_desktop, @@ -672,15 +683,12 @@ def __init__( aedt_process_id, ) elif starting_mode == "grpc": - self._logger.info("Launching PyAEDT outside AEDT with gRPC plugin.") + self._logger.info("Launching PyAEDT with gRPC plugin.") self._init_grpc(non_graphical, new_desktop, version, student_version_flag, version_key) self._set_logger_file() settings.enable_desktop_logs = not self.non_graphical self._init_desktop() - self._logger.info("pyaedt v%s", pyaedt_version) - if not settings.remote_api: - self._logger.info("Python version %s", sys.version) current_pid = int(self.odesktop.GetProcessID()) if aedt_process_id and not new_desktop and aedt_process_id != current_pid: # pragma no cover @@ -700,8 +708,6 @@ def __init__( self.aedt_version_id = self.odesktop.GetVersion()[0:6] - self._logger.info("AEDT %s Build Date %s", self.odesktop.GetVersion(), self.odesktop.GetBuildDateTimeString()) - if is_ironpython: # pragma no cover sys.path.append(os.path.join(settings.aedt_install_dir, "common", "commonfiles", "IronPython", "DLLs")) if "GetGrpcServerPort" in dir(self.odesktop): @@ -982,12 +988,7 @@ def _init_dotnet( if proc == processID2 and len(processID2) > 1: self._dispatch_win32(version) elif version_key >= "2021.2": - if student_version: - self.logger.info("AEDT {} Student version started with process ID {}.".format(version_key, proc[0])) - elif aedt_process_id: - self.logger.info("Connecting to AEDT session with process ID {}.".format(proc[0])) - else: - self.logger.info("AEDT {} Started with process ID {}.".format(version_key, proc[0])) + context = pythoncom.CreateBindCtx(0) running_coms = pythoncom.GetRunningObjectTable() monikiers = running_coms.EnumRunning() @@ -999,6 +1000,12 @@ def _init_dotnet( from pyaedt.generic.clr_module import win32_client self.odesktop = win32_client.Dispatch(obj.QueryInterface(pythoncom.IID_IDispatch)) + if student_version: + self.logger.info("New AEDT {} Student version process ID {}.".format(version_key, proc[0])) + elif aedt_process_id: + self.logger.info("Existing AEDT session process ID {}.".format(proc[0])) + else: + self.logger.info("New AEDT {} Started process ID {}.".format(version_key, proc[0])) break else: self.logger.warning( @@ -1030,6 +1037,8 @@ def _initialize( else: return StandalonePyScriptWrapper.CreateObject(version) else: + settings.use_grpc_api = True + self.is_grpc_api = True base_path = settings.aedt_install_dir sys.path.insert(0, base_path) sys.path.insert(0, os.path.join(base_path, "PythonFiles", "DesktopPlugin")) @@ -1051,7 +1060,6 @@ def _initialize( self.isoutsideDesktop = True self.aedt_process_id = self.odesktop.GetProcessID() - self.is_grpc_api = True return True @property @@ -1066,10 +1074,16 @@ def odesktop(self): >>> d = Desktop() >>> d.odesktop """ - try: - return self.grpc_plugin.odesktop - except Exception: - return self._odesktop + if settings.use_grpc_api: + tries = 0 + while tries < 5: + try: + self._odesktop = self.grpc_plugin.odesktop + return self._odesktop + except Exception: # pragma: no cover + tries += 1 + time.sleep(1) + return self._odesktop # pragma: no cover @odesktop.setter def odesktop(self, val): # pragma: no cover diff --git a/pyaedt/generic/configurations.py b/pyaedt/generic/configurations.py index ff8bf97e1c6..62bb5c44583 100644 --- a/pyaedt/generic/configurations.py +++ b/pyaedt/generic/configurations.py @@ -714,7 +714,18 @@ def __init__(self, app): self._app = app self.options = ConfigurationsOptions() self.results = ImportResults() + self._schema = None + @property + def schema(self): + """Schema dictionary. + + Returns + ------- + dict + """ + if self._schema: + return self._schema pyaedt_installed_path = os.path.dirname(pyaedt.__file__) schema_bytes = None @@ -732,6 +743,7 @@ def __init__(self, app): else: # pragma: no cover self._app.logger.error("Failed to load configuration schema.") self._schema = None + return self._schema @staticmethod @pyaedt_function_handler() @@ -1069,7 +1081,7 @@ def validate(self, config): self._app.logger.warning("Iron Python: Unable to validate json Schema.") else: try: - validate(instance=config_data, schema=self._schema) + validate(instance=config_data, schema=self.schema) return True except exceptions.ValidationError as e: self._app.logger.warning("Configuration is invalid.") diff --git a/pyaedt/generic/general_methods.py b/pyaedt/generic/general_methods.py index 7d06b7d67cc..d5c07d9a4ab 100644 --- a/pyaedt/generic/general_methods.py +++ b/pyaedt/generic/general_methods.py @@ -66,6 +66,7 @@ "Rename", "RestoreProjectArchive", "ImportGerber", + "EditSources", ] diff --git a/pyaedt/generic/grpc_plugin_dll_class.py b/pyaedt/generic/grpc_plugin_dll_class.py index f9504503aa0..f51a259f886 100644 --- a/pyaedt/generic/grpc_plugin_dll_class.py +++ b/pyaedt/generic/grpc_plugin_dll_class.py @@ -366,9 +366,7 @@ def CreateAedtApplication(self, machine="", port=0, NGmode=False, alwaysNew=True @property def odesktop(self): - if settings.use_multi_desktop: - self.aedt = self.recreate_application() - return self.aedt.GetAppDesktop() + return self.recreate_application() def recreate_application(self, force=False): def run(): @@ -379,14 +377,15 @@ def run(): self.port = port self.machine = machine self.aedt = self.AedtAPI.CreateAedtApplication(self.machine, self.port, self.non_graphical, False) - return self.aedt + return self.aedt.GetAppDesktop() if force: return run() else: try: - self.aedt.GetAppDesktop() - return self.aedt + odesktop = self.aedt.GetAppDesktop() + if odesktop: + return odesktop except Exception: return run()