diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 67b51def..5ae3768d 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,19 +310,18 @@ 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() + 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: + 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: 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 subprocess.TimeoutExpired: + raise ModelicaSystemError(f"Timeout running command {repr(cmd)}") except Exception as e: raise ModelicaSystemError(f"Exception {type(e)} running command {cmd}: {e}") @@ -675,7 +674,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 @@ -738,7 +737,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 @@ -1055,13 +1054,15 @@ 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: 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 @@ -1116,7 +1117,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"