From 4dc78c236def1ec230b7f9ec500af841e0c3b96e Mon Sep 17 00:00:00 2001 From: syntron Date: Thu, 24 Apr 2025 20:59:31 +0200 Subject: [PATCH 1/5] [ModelicaSystem] simplify subprocess.Popen() => use subprocess.run() --- OMPython/ModelicaSystem.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 67b51def..6b825a6c 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -310,19 +310,13 @@ def _run_cmd(self, cmd: list): my_env = None try: - p = subprocess.Popen(cmd, env=my_env, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, cwd=self.tempdir) - stdout, stderr = p.communicate() - - stdout = stdout.decode('ascii').strip() - stderr = stderr.decode('ascii').strip() - if stderr: + cmdres = subprocess.run(cmd, capture_output=True, text=True, env=my_env, cwd=self.tempdir) + stdout = cmdres.stdout.strip() + stderr = cmdres.stderr.strip() + if cmdres.returncode != 0 or stderr: raise ModelicaSystemError(f"Error running command {cmd}: {stderr}") if self._verbose and stdout: logger.info("OM output for command %s:\n%s", cmd, stdout) - # check process returncode, some errors don't print to stderr - if p.wait(): - raise ModelicaSystemError(f"Error running command {cmd}: nonzero returncode") except Exception as e: raise ModelicaSystemError(f"Exception {type(e)} running command {cmd}: {e}") From 26588c7a599d9b0e2c241ca0145821c2a057cfdb Mon Sep 17 00:00:00 2001 From: syntron Date: Thu, 24 Apr 2025 21:00:45 +0200 Subject: [PATCH 2/5] [ModelicaSystem] add timeout to subprocess.run() in _run_cmd() --- OMPython/ModelicaSystem.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 6b825a6c..820baaa0 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -287,7 +287,7 @@ def setTempDirectory(self, customBuildDirectory): def getWorkDirectory(self): return self.tempdir - def _run_cmd(self, cmd: list): + def _run_cmd(self, cmd: list, timeout: Optional[int] = None): logger.debug("Run OM command %s in %s", cmd, self.tempdir) if platform.system() == "Windows": @@ -310,13 +310,16 @@ def _run_cmd(self, cmd: list): my_env = None try: - cmdres = subprocess.run(cmd, capture_output=True, text=True, env=my_env, cwd=self.tempdir) + cmdres = subprocess.run(cmd, capture_output=True, text=True, env=my_env, cwd=self.tempdir, + timeout=timeout) stdout = cmdres.stdout.strip() stderr = cmdres.stderr.strip() if cmdres.returncode != 0 or stderr: raise ModelicaSystemError(f"Error running command {cmd}: {stderr}") if self._verbose and stdout: logger.info("OM output for command %s:\n%s", cmd, stdout) + except subprocess.TimeoutExpired: + raise ModelicaSystemError(f"Timeout running command {repr(cmd)}") except Exception as e: raise ModelicaSystemError(f"Exception {type(e)} running command {cmd}: {e}") @@ -669,7 +672,7 @@ def get_exe_file(self) -> pathlib.Path: else: return pathlib.Path(self.tempdir) / self.modelName - def simulate(self, resultfile=None, simflags=None): # 11 + def simulate(self, resultfile=None, simflags=None, timeout: Optional[int] = None): # 11 """ This method simulates model according to the simulation options. usage @@ -732,7 +735,7 @@ def simulate(self, resultfile=None, simflags=None): # 11 cmd = exe_file.as_posix() + override + csvinput + r + simflags cmd = [s for s in cmd.split(' ') if s] - self._run_cmd(cmd=cmd) + self._run_cmd(cmd=cmd, timeout=timeout) self.simulationFlag = True # to extract simulation results @@ -1049,7 +1052,8 @@ def optimize(self): # 21 return optimizeResult - def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = None) -> LinearizationResult: + def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = None, + timeout: Optional[int] = None) -> LinearizationResult: """Linearize the model according to linearOptions. Args: @@ -1110,7 +1114,7 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N else: cmd = exe_file.as_posix() + linruntime + override + csvinput + simflags cmd = [s for s in cmd.split(' ') if s] - self._run_cmd(cmd=cmd) + self._run_cmd(cmd=cmd, timeout=timeout) # code to get the matrix and linear inputs, outputs and states linearFile = pathlib.Path(self.tempdir) / "linearized_model.py" From b0aa3d5e91b6c73d2fd8a70de5d4cc77793a7e2b Mon Sep 17 00:00:00 2001 From: syntron Date: Fri, 25 Apr 2025 22:47:15 +0200 Subject: [PATCH 3/5] [ModelicaSystem._run_cmd()] differentiate between OM error and nonzero return code --- OMPython/ModelicaSystem.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 820baaa0..6c515fb3 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -314,7 +314,9 @@ def _run_cmd(self, cmd: list, timeout: Optional[int] = None): timeout=timeout) stdout = cmdres.stdout.strip() stderr = cmdres.stderr.strip() - if cmdres.returncode != 0 or stderr: + if cmdres.returncode != 0: + raise ModelicaSystemError(f"Error running command {cmd}: nonzero return code") + if stderr: raise ModelicaSystemError(f"Error running command {cmd}: {stderr}") if self._verbose and stdout: logger.info("OM output for command %s:\n%s", cmd, stdout) From 863d6b62d7ff72d5ed2fcde1dfd855c00a73231b Mon Sep 17 00:00:00 2001 From: syntron Date: Fri, 25 Apr 2025 23:15:34 +0200 Subject: [PATCH 4/5] [ModelicaSystem.linearize()] fix docstring / add description for timeout --- OMPython/ModelicaSystem.py | 1 + 1 file changed, 1 insertion(+) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 6c515fb3..3954b0c8 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -1062,6 +1062,7 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N lintime: Override linearOptions["stopTime"] value. simflags: A string of extra command line flags for the model binary. + timeout: Possible timeout for the execution of OM. Returns: A LinearizationResult object is returned. This allows several From a1b6aa8fb63b689da0e2fdf4254eba593a19d3cb Mon Sep 17 00:00:00 2001 From: syntron Date: Sat, 3 May 2025 21:00:07 +0200 Subject: [PATCH 5/5] [ModelicaSystem] provide return code in log message --- OMPython/ModelicaSystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 3954b0c8..5ae3768d 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -315,7 +315,7 @@ def _run_cmd(self, cmd: list, timeout: Optional[int] = None): stdout = cmdres.stdout.strip() stderr = cmdres.stderr.strip() if cmdres.returncode != 0: - raise ModelicaSystemError(f"Error running command {cmd}: nonzero return code") + raise ModelicaSystemError(f"Error running command {cmd}: return code = {cmdres.returncode}") if stderr: raise ModelicaSystemError(f"Error running command {cmd}: {stderr}") if self._verbose and stdout: