diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index c5511923..24be4f7b 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -282,14 +282,14 @@ def omcpath_tempdir(self, tempdir_base: Optional[OMPathABC] = None) -> OMPathABC def execute(self, command: str): return self.omc_process.execute(command=command) - def sendExpression(self, command: str, parsed: bool = True) -> Any: + def sendExpression(self, command: str, parsed: bool = True, raise_on_error: bool = True) -> Any: """ Send an expression to the OMC server and return the result. The complete error handling of the OMC result is done within this method using '"getMessagesStringInternal()'. Caller should only check for OMSessionException. """ - return self.omc_process.sendExpression(expr=command, parsed=parsed) + return self.omc_process.sendExpression(expr=command, parsed=parsed, raise_on_error=raise_on_error) def get_version(self) -> str: return self.omc_process.get_version() diff --git a/OMPython/modelica_system_omc.py b/OMPython/modelica_system_omc.py index 34805e0f..e9c9409d 100644 --- a/OMPython/modelica_system_omc.py +++ b/OMPython/modelica_system_omc.py @@ -203,7 +203,11 @@ def buildModel(self, variableFilter: Optional[str] = None): else: var_filter = 'variableFilter=".*"' - build_model_result = self._requestApi(apiName="buildModel", entity=self._model_name, properties=var_filter) + # buildModel() can emit 'error'-level diagnostics (e.g. a structurally singular initialization + # system) that OMC itself recovers from without actually failing the build. Don't raise on those + # here; check_model_executable()/_xmlparse() below independently verify the build really succeeded. + build_model_result = self._requestApi(apiName="buildModel", entity=self._model_name, properties=var_filter, + raise_on_error=False) logger.debug("OM model build result: %s", build_model_result) # check if the executable exists ... @@ -212,12 +216,12 @@ def buildModel(self, variableFilter: Optional[str] = None): xml_file = self._session.omcpath(build_model_result[0]).parent / build_model_result[1] self._xmlparse(xml_file=xml_file) - def sendExpression(self, expr: str, parsed: bool = True) -> Any: + def sendExpression(self, expr: str, parsed: bool = True, raise_on_error: bool = True) -> Any: """ Wrapper for OMCSession.sendExpression(). """ try: - retval = self._session.sendExpression(expr=expr, parsed=parsed) + retval = self._session.sendExpression(expr=expr, parsed=parsed, raise_on_error=raise_on_error) except OMSessionException as ex: raise ModelicaSystemError(f"Error executing {repr(expr)}: {ex}") from ex @@ -231,6 +235,7 @@ def _requestApi( apiName: str, entity: Optional[str] = None, properties: Optional[str] = None, + raise_on_error: bool = True, ) -> Any: if entity is not None and properties is not None: expr = f'{apiName}({entity}, {properties})' @@ -242,7 +247,7 @@ def _requestApi( else: expr = f'{apiName}()' - return self.sendExpression(expr=expr) + return self.sendExpression(expr=expr, raise_on_error=raise_on_error) def getContinuousFinal( self, diff --git a/OMPython/om_session_abc.py b/OMPython/om_session_abc.py index 70e897d7..2def56e4 100644 --- a/OMPython/om_session_abc.py +++ b/OMPython/om_session_abc.py @@ -317,7 +317,10 @@ def _tempdir(tempdir_base: OMPathABC) -> OMPathABC: return tempdir @abc.abstractmethod - def sendExpression(self, expr: str, parsed: bool = True) -> Any: + def sendExpression(self, expr: str, parsed: bool = True, raise_on_error: bool = True) -> Any: """ Function needed to send expressions to the OMC server via ZMQ. + + If raise_on_error is False, 'error'-level OMC diagnostics are logged instead of raised as an + OMSessionException; use this only when the caller has its own, more precise way of verifying success. """ diff --git a/OMPython/om_session_omc.py b/OMPython/om_session_omc.py index 6626cd17..53b709e6 100644 --- a/OMPython/om_session_omc.py +++ b/OMPython/om_session_omc.py @@ -387,12 +387,18 @@ def execute(self, command: str): return self.sendExpression(command, parsed=False) - def sendExpression(self, expr: str, parsed: bool = True) -> Any: + def sendExpression(self, expr: str, parsed: bool = True, raise_on_error: bool = True) -> Any: """ Send an expression to the OMC server and return the result. The complete error handling of the OMC result is done within this method using 'getMessagesStringInternal()'. Caller should only check for OMSessionException. + + Some OMC API calls (e.g. buildModel) can emit 'error'-level diagnostics that are recoverable and don't + actually prevent the call from succeeding (e.g. a structurally singular initialization system that OMC + resolves via a fallback). Callers who have their own, more precise way of verifying success (such as + checking that the resulting files/executable actually exist) can pass raise_on_error=False to have such + messages logged instead of raised as an exception. """ if self._omc_zmq is None: @@ -509,8 +515,11 @@ def sendExpression(self, expr: str, parsed: bool = True) -> Any: msg_long_list.append(msg_long) if has_error: msg_long_str = '\n'.join(f"{idx:02d}: {msg}" for idx, msg in enumerate(msg_long_list)) - raise OMSessionException(f"OMC error occurred for 'sendExpression(expr={expr}, parsed={parsed}):\n" - f"{msg_long_str}") + if raise_on_error: + raise OMSessionException( + f"OMC error occurred for 'sendExpression(expr={expr}, parsed={parsed}):\n{msg_long_str}") + logger.warning("OMC reported 'error'-level messages for 'sendExpression(expr=%s, parsed=%s)', but " + "raise_on_error=False was requested; continuing:\n%s", expr, parsed, msg_long_str) if not parsed: return result diff --git a/OMPython/om_session_runner.py b/OMPython/om_session_runner.py index fc8e5ac8..470e2aaf 100644 --- a/OMPython/om_session_runner.py +++ b/OMPython/om_session_runner.py @@ -379,5 +379,5 @@ def omcpath_tempdir(self, tempdir_base: Optional[OMPathABC] = None) -> OMPathABC return self._tempdir(tempdir_base=tempdir_base) - def sendExpression(self, expr: str, parsed: bool = True) -> Any: + def sendExpression(self, expr: str, parsed: bool = True, raise_on_error: bool = True) -> Any: raise OMSessionException(f"{self.__class__.__name__} does not uses an OMC server!")