Skip to content

Commit 675a2ad

Browse files
authored
[OMCSessionRunData] run executable via OMCSessionZMQ (#331)
* [OMCSessionRunData] add new class to store all information about a model executable * [OMCSessionRunData] use class to move run of model executable to OMSessionZMQ * [test_ModelicaSystemCmd] fix test * [OMCSessionRunData] add to __init__ * [test_ModelicaSystemCmd] fix test (again)
1 parent 7620bcc commit 675a2ad

File tree

4 files changed

+207
-78
lines changed

4 files changed

+207
-78
lines changed

OMPython/ModelicaSystem.py

Lines changed: 36 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,12 @@
3838
import numbers
3939
import numpy as np
4040
import os
41-
import platform
42-
import re
43-
import subprocess
4441
import textwrap
4542
from typing import Optional, Any
4643
import warnings
4744
import xml.etree.ElementTree as ET
4845

49-
from OMPython.OMCSession import OMCSessionException, OMCSessionZMQ, OMCProcessLocal, OMCPath
46+
from OMPython.OMCSession import OMCSessionException, OMCSessionRunData, OMCSessionZMQ, OMCProcessLocal, OMCPath
5047

5148
# define logger using the current module name as ID
5249
logger = logging.getLogger(__name__)
@@ -112,7 +109,14 @@ def __getitem__(self, index: int):
112109
class ModelicaSystemCmd:
113110
"""A compiled model executable."""
114111

115-
def __init__(self, runpath: OMCPath, modelname: str, timeout: Optional[float] = None) -> None:
112+
def __init__(
113+
self,
114+
session: OMCSessionZMQ,
115+
runpath: OMCPath,
116+
modelname: str,
117+
timeout: Optional[float] = None,
118+
) -> None:
119+
self._session = session
116120
self._runpath = runpath
117121
self._model_name = modelname
118122
self._timeout = timeout
@@ -227,27 +231,12 @@ def args_set(
227231
for arg in args:
228232
self.arg_set(key=arg, val=args[arg])
229233

230-
def get_exe(self) -> OMCPath:
231-
"""Get the path to the compiled model executable."""
232-
if platform.system() == "Windows":
233-
path_exe = self._runpath / f"{self._model_name}.exe"
234-
else:
235-
path_exe = self._runpath / self._model_name
236-
237-
if not path_exe.exists():
238-
raise ModelicaSystemError(f"Application file path not found: {path_exe}")
239-
240-
return path_exe
241-
242-
def get_cmd(self) -> list:
243-
"""Get a list with the path to the executable and all command line args.
244-
245-
This can later be used as an argument for subprocess.run().
234+
def get_cmd_args(self) -> list[str]:
235+
"""
236+
Get a list with the command arguments for the model executable.
246237
"""
247238

248-
path_exe = self.get_exe()
249-
250-
cmdl = [path_exe.as_posix()]
239+
cmdl = []
251240
for key in sorted(self._args):
252241
if self._args[key] is None:
253242
cmdl.append(f"-{key}")
@@ -256,54 +245,26 @@ def get_cmd(self) -> list:
256245

257246
return cmdl
258247

259-
def run(self) -> int:
260-
"""Run the requested simulation.
261-
262-
Returns
263-
-------
264-
Subprocess return code (0 on success).
248+
def definition(self) -> OMCSessionRunData:
265249
"""
250+
Define all needed data to run the model executable. The data is stored in an OMCSessionRunData object.
251+
"""
252+
# ensure that a result filename is provided
253+
result_file = self.arg_get('r')
254+
if not isinstance(result_file, str):
255+
result_file = (self._runpath / f"{self._model_name}.mat").as_posix()
256+
257+
omc_run_data = OMCSessionRunData(
258+
cmd_path=self._runpath.as_posix(),
259+
cmd_model_name=self._model_name,
260+
cmd_args=self.get_cmd_args(),
261+
cmd_result_path=result_file,
262+
cmd_timeout=self._timeout,
263+
)
266264

267-
cmdl: list = self.get_cmd()
268-
269-
logger.debug("Run OM command %s in %s", repr(cmdl), self._runpath.as_posix())
270-
271-
if platform.system() == "Windows":
272-
path_dll = ""
273-
274-
# set the process environment from the generated .bat file in windows which should have all the dependencies
275-
path_bat = self._runpath / f"{self._model_name}.bat"
276-
if not path_bat.exists():
277-
raise ModelicaSystemError("Batch file (*.bat) does not exist " + str(path_bat))
278-
279-
with open(file=path_bat, mode='r', encoding='utf-8') as fh:
280-
for line in fh:
281-
match = re.match(r"^SET PATH=([^%]*)", line, re.IGNORECASE)
282-
if match:
283-
path_dll = match.group(1).strip(';') # Remove any trailing semicolons
284-
my_env = os.environ.copy()
285-
my_env["PATH"] = path_dll + os.pathsep + my_env["PATH"]
286-
else:
287-
# TODO: how to handle path to resources of external libraries for any system not Windows?
288-
my_env = None
289-
290-
try:
291-
cmdres = subprocess.run(cmdl, capture_output=True, text=True, env=my_env, cwd=self._runpath,
292-
timeout=self._timeout, check=True)
293-
stdout = cmdres.stdout.strip()
294-
stderr = cmdres.stderr.strip()
295-
returncode = cmdres.returncode
296-
297-
logger.debug("OM output for command %s:\n%s", repr(cmdl), stdout)
298-
299-
if stderr:
300-
raise ModelicaSystemError(f"Error running command {repr(cmdl)}: {stderr}")
301-
except subprocess.TimeoutExpired as ex:
302-
raise ModelicaSystemError(f"Timeout running command {repr(cmdl)}") from ex
303-
except subprocess.CalledProcessError as ex:
304-
raise ModelicaSystemError(f"Error running command {repr(cmdl)}") from ex
265+
omc_run_data_updated = self._session.omc_run_data_update(omc_run_data=omc_run_data)
305266

306-
return returncode
267+
return omc_run_data_updated
307268

308269
@staticmethod
309270
def parse_simflags(simflags: str) -> dict[str, Optional[str | dict[str, Any] | numbers.Number]]:
@@ -1031,6 +992,7 @@ def simulate_cmd(
1031992
"""
1032993

1033994
om_cmd = ModelicaSystemCmd(
995+
session=self._getconn,
1034996
runpath=self.getWorkDirectory(),
1035997
modelname=self._model_name,
1036998
timeout=timeout,
@@ -1127,7 +1089,8 @@ def simulate(
11271089
if self._result_file.is_file():
11281090
self._result_file.unlink()
11291091
# ... run simulation ...
1130-
returncode = om_cmd.run()
1092+
cmd_definition = om_cmd.definition()
1093+
returncode = self._getconn.run_model_executable(cmd_run_data=cmd_definition)
11311094
# and check returncode *AND* resultfile
11321095
if returncode != 0 and self._result_file.is_file():
11331096
# check for an empty (=> 0B) result file which indicates a crash of the model executable
@@ -1679,6 +1642,7 @@ def linearize(
16791642
)
16801643

16811644
om_cmd = ModelicaSystemCmd(
1645+
session=self._getconn,
16821646
runpath=self.getWorkDirectory(),
16831647
modelname=self._model_name,
16841648
timeout=timeout,
@@ -1716,7 +1680,8 @@ def linearize(
17161680
linear_file = self.getWorkDirectory() / "linearized_model.py"
17171681
linear_file.unlink(missing_ok=True)
17181682

1719-
returncode = om_cmd.run()
1683+
cmd_definition = om_cmd.definition()
1684+
returncode = self._getconn.run_model_executable(cmd_run_data=cmd_definition)
17201685
if returncode != 0:
17211686
raise ModelicaSystemError(f"Linearize failed with return code: {returncode}")
17221687
if not linear_file.is_file():

0 commit comments

Comments
 (0)