Skip to content

Commit 64a16ef

Browse files
authored
Finish error handling (#278)
* Revert "[ModelicaSystem] remove OMCSessionException for now" This reverts commit 2c3ab36. * [OMCSessionZMQ] allways check for errors if using sendExpression() needs the preparation / additional changes in OMCSession* and ModelicaSystem * [ModelicaSystem] exception handling for sendExpression() * [ModelicaSystem] remove last call to getErrorString() this is handled in OMCSessionZMQ.sendExpression() * [OMCSessionZMQ] use 'getMessagesStringInternal()' to check for OMC errors for each command using sendExpression() * [OMCSessionZMQ] raise error if parsing of send Expression() result fails * [ModelicaSystem] do not print all output of OM command executed
1 parent 53bb6ff commit 64a16ef

File tree

2 files changed

+73
-8
lines changed

2 files changed

+73
-8
lines changed

OMPython/ModelicaSystem.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@
4444
import importlib
4545
import pathlib
4646
from dataclasses import dataclass
47+
import textwrap
4748
from typing import Optional
4849

49-
from OMPython.OMCSession import OMCSessionZMQ
50+
from OMPython.OMCSession import OMCSessionZMQ, OMCSessionException
5051

5152
# define logger using the current module name as ID
5253
logger = logging.getLogger(__name__)
@@ -332,8 +333,14 @@ def buildModel(self, variableFilter=None):
332333
self.xmlparse()
333334

334335
def sendExpression(self, expr, parsed=True):
335-
logger.debug("sendExpression(%r, %r)", expr, parsed)
336-
return self.getconn.sendExpression(expr, parsed)
336+
try:
337+
retval = self.getconn.sendExpression(expr, parsed)
338+
except OMCSessionException as ex:
339+
raise ModelicaSystemError(f"Error executing {repr(expr)}") from ex
340+
341+
logger.debug(f"Result of executing {repr(expr)}: {textwrap.shorten(repr(retval), width=100)}")
342+
343+
return retval
337344

338345
# request to OMC
339346
def requestApi(self, apiName, entity=None, properties=None): # 2
@@ -425,7 +432,7 @@ def getContinuous(self, names=None): # 4
425432
try:
426433
value = self.getSolutions(i)
427434
self.continuouslist[i] = value[0][-1]
428-
except Exception as ex:
435+
except OMCSessionException as ex:
429436
raise ModelicaSystemError(f"{i} could not be computed") from ex
430437
return self.continuouslist
431438

@@ -1093,8 +1100,7 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N
10931100
linearFile = pathlib.Path(f'linear_{self.modelName}.py')
10941101

10951102
if not linearFile.exists():
1096-
errormsg = self.sendExpression("getErrorString()")
1097-
raise ModelicaSystemError(f"Linearization failed: {linearFile} not found: {errormsg}")
1103+
raise ModelicaSystemError(f"Linearization failed: {linearFile} not found!")
10981104

10991105
# this function is called from the generated python code linearized_model.py at runtime,
11001106
# to improve the performance by directly reading the matrices A, B, C and D from the julia code and avoid building the linearized modelica model

OMPython/OMCSession.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import os
4242
import pathlib
4343
import psutil
44+
import re
4445
import signal
4546
import subprocess
4647
import sys
@@ -325,6 +326,9 @@ def __init__(self, timeout=10.00,
325326
# connect to the running omc instance using ZMQ
326327
self._connect_to_omc(timeout)
327328

329+
self._re_log_entries = None
330+
self._re_log_raw = None
331+
328332
def __del__(self):
329333
try:
330334
self.sendExpression("quit()")
@@ -549,6 +553,62 @@ def sendExpression(self, command, parsed=True):
549553
return None
550554
else:
551555
result = self._omc.recv_string()
556+
557+
if command == "getErrorString()":
558+
# no error handling if 'getErrorString()' is called
559+
pass
560+
elif command == "getMessagesStringInternal()":
561+
# no error handling if 'getMessagesStringInternal()' is called; parsing NOT possible!
562+
if parsed:
563+
logger.warning("Result of 'getMessagesStringInternal()' cannot be parsed - set parsed to False!")
564+
parsed = False
565+
else:
566+
# allways check for error
567+
self._omc.send_string('getMessagesStringInternal()', flags=zmq.NOBLOCK)
568+
error_raw = self._omc.recv_string()
569+
# run error handling only if there is something to check
570+
if error_raw != "{}\n":
571+
if not self._re_log_entries:
572+
self._re_log_entries = re.compile(pattern=r'record OpenModelica\.Scripting\.ErrorMessage'
573+
'(.*?)'
574+
r'end OpenModelica\.Scripting\.ErrorMessage;',
575+
flags=re.MULTILINE | re.DOTALL)
576+
if not self._re_log_raw:
577+
self._re_log_raw = re.compile(
578+
pattern=r"\s+message = \"(.*?)\",\n" # message
579+
r"\s+kind = .OpenModelica.Scripting.ErrorKind.(.*?),\n" # kind
580+
r"\s+level = .OpenModelica.Scripting.ErrorLevel.(.*?),\n" # level
581+
r"\s+id = (.*?)" # id
582+
"(,\n|\n)", # end marker
583+
flags=re.MULTILINE | re.DOTALL)
584+
585+
# extract all ErrorMessage records
586+
log_entries = self._re_log_entries.findall(string=error_raw)
587+
for log_entry in reversed(log_entries):
588+
log_raw = self._re_log_raw.findall(string=log_entry)
589+
if len(log_raw) != 1 or len(log_raw[0]) != 5:
590+
logger.warning("Invalid ErrorMessage record returned by 'getMessagesStringInternal()':"
591+
f" {repr(log_entry)}!")
592+
593+
log_message = log_raw[0][0].encode().decode('unicode_escape')
594+
log_kind = log_raw[0][1]
595+
log_level = log_raw[0][2]
596+
log_id = log_raw[0][3]
597+
598+
msg = (f"[OMC log for 'sendExpression({command}, {parsed})']: "
599+
f"[{log_kind}:{log_level}:{log_id}] {log_message}")
600+
601+
# response according to the used log level
602+
# see: https://build.openmodelica.org/Documentation/OpenModelica.Scripting.ErrorLevel.html
603+
if log_level == 'error':
604+
raise OMCSessionException(msg)
605+
elif log_level == 'warning':
606+
logger.warning(msg)
607+
elif log_level == 'notification':
608+
logger.info(msg)
609+
else: # internal
610+
logger.debug(msg)
611+
552612
if parsed is True:
553613
try:
554614
return om_parser_typed(result)
@@ -557,7 +617,6 @@ def sendExpression(self, command, parsed=True):
557617
try:
558618
return om_parser_basic(result)
559619
except (TypeError, UnboundLocalError) as ex:
560-
logger.warning('OMParser error: %s. Returning the unparsed result.', ex)
561-
return result
620+
raise OMCSessionException("Cannot parse OMC result") from ex
562621
else:
563622
return result

0 commit comments

Comments
 (0)