Skip to content

Commit c19cc78

Browse files
authored
Merge branch 'master' into flake8
2 parents 8e736f7 + 35e8b4f commit c19cc78

4 files changed

Lines changed: 235 additions & 78 deletions

File tree

OMPython/ModelicaSystem.py

Lines changed: 64 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]]:
@@ -1032,6 +993,7 @@ def simulate_cmd(
1032993
"""
1033994

1034995
om_cmd = ModelicaSystemCmd(
996+
session=self._getconn,
1035997
runpath=self.getWorkDirectory(),
1036998
modelname=self._model_name,
1037999
timeout=timeout,
@@ -1130,7 +1092,8 @@ def simulate(
11301092
if self._result_file.is_file():
11311093
self._result_file.unlink()
11321094
# ... run simulation ...
1133-
returncode = om_cmd.run()
1095+
cmd_definition = om_cmd.definition()
1096+
returncode = self._getconn.run_model_executable(cmd_run_data=cmd_definition)
11341097
# and check returncode *AND* resultfile
11351098
if returncode != 0 and self._result_file.is_file():
11361099
# check for an empty (=> 0B) result file which indicates a crash of the model executable
@@ -1144,6 +1107,34 @@ def simulate(
11441107

11451108
self._simulated = True
11461109

1110+
def plot(
1111+
self,
1112+
plotdata: str,
1113+
resultfile: Optional[str | os.PathLike] = None,
1114+
) -> None:
1115+
"""
1116+
Plot a variable using OMC; this will work for local OMC usage only (OMCProcessLocal). The reason is that the
1117+
plot is created by OMC which needs access to the local display. This is not the case for docker and WSL.
1118+
"""
1119+
1120+
if not isinstance(self._getconn.omc_process, OMCProcessLocal):
1121+
raise ModelicaSystemError("Plot is using the OMC plot functionality; "
1122+
"thus, it is only working if OMC is running locally!")
1123+
1124+
if resultfile is not None:
1125+
plot_result_file = self._getconn.omcpath(resultfile)
1126+
elif self._result_file is not None:
1127+
plot_result_file = self._result_file
1128+
else:
1129+
raise ModelicaSystemError("No resultfile available - either run simulate() before plotting "
1130+
"or provide a result file!")
1131+
1132+
if not plot_result_file.is_file():
1133+
raise ModelicaSystemError(f"Provided resultfile {repr(plot_result_file.as_posix())} does not exists!")
1134+
1135+
expr = f'plot({plotdata}, fileName="{plot_result_file.as_posix()}")'
1136+
self.sendExpression(expr=expr)
1137+
11471138
def getSolutions(
11481139
self,
11491140
varList: Optional[str | list[str]] = None,
@@ -1687,6 +1678,7 @@ def linearize(
16871678
)
16881679

16891680
om_cmd = ModelicaSystemCmd(
1681+
session=self._getconn,
16901682
runpath=self.getWorkDirectory(),
16911683
modelname=self._model_name,
16921684
timeout=timeout,
@@ -1724,7 +1716,8 @@ def linearize(
17241716
linear_file = self.getWorkDirectory() / "linearized_model.py"
17251717
linear_file.unlink(missing_ok=True)
17261718

1727-
returncode = om_cmd.run()
1719+
cmd_definition = om_cmd.definition()
1720+
returncode = self._getconn.run_model_executable(cmd_run_data=cmd_definition)
17281721
if returncode != 0:
17291722
raise ModelicaSystemError(f"Linearize failed with return code: {returncode}")
17301723
if not linear_file.is_file():

0 commit comments

Comments
 (0)