From d37ec0bf39a04ebc2344091634309dd06e883042 Mon Sep 17 00:00:00 2001 From: syntron Date: Sun, 23 Nov 2025 14:14:39 +0100 Subject: [PATCH 1/7] [ModelicaSystem*] add timeout argument --- OMPython/ModelicaSystem.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 1a979647..9a5b59f9 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -337,6 +337,7 @@ def __init__( work_directory: Optional[str | os.PathLike] = None, omhome: Optional[str] = None, omc_process: Optional[OMCProcess] = None, + timeout: float = 10.0, ) -> None: """Create a ModelicaSystem instance. To define the model use model() or convertFmu2Mo(). @@ -350,6 +351,7 @@ def __init__( omhome: path to OMC to be used when creating the OMC session (see OMCSessionZMQ). omc_process: definition of a (local) OMC process to be used. If unspecified, a new local session will be created. + timeout: float value to define the timeout; if nothing is defined, a default value of 10s is used """ self._quantities: list[dict[str, Any]] = [] @@ -379,7 +381,7 @@ def __init__( if omc_process is not None: self._session = OMCSessionZMQ(omc_process=omc_process) else: - self._session = OMCSessionZMQ(omhome=omhome) + self._session = OMCSessionZMQ(omhome=omhome, timeout=timeout) # set commandLineOptions using default values or the user defined list if command_line_options is None: @@ -1968,10 +1970,10 @@ def __init__( work_directory: Optional[str | os.PathLike] = None, omhome: Optional[str] = None, omc_process: Optional[OMCProcess] = None, + timeout: float = 10.0, # simulation specific input # TODO: add more settings (simulation options, input options, ...) simargs: Optional[dict[str, Optional[str | dict[str, str] | numbers.Number]]] = None, - timeout: Optional[int] = None, # DoE specific inputs resultpath: Optional[str | os.PathLike] = None, parameters: Optional[dict[str, list[str] | list[int] | list[float]]] = None, @@ -1989,6 +1991,7 @@ def __init__( work_directory=work_directory, omhome=omhome, omc_process=omc_process, + timeout=timeout, ) self._mod.model( model_file=model_file, From d58f583d5e53d8a5d7bdf1bc9ec6117ab353277f Mon Sep 17 00:00:00 2001 From: syntron Date: Tue, 25 Nov 2025 21:47:35 +0100 Subject: [PATCH 2/7] [ModelicaSystem*] remove timeout variable(s) --- OMPython/ModelicaSystem.py | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/OMPython/ModelicaSystem.py b/OMPython/ModelicaSystem.py index 9a5b59f9..d6a3d654 100644 --- a/OMPython/ModelicaSystem.py +++ b/OMPython/ModelicaSystem.py @@ -130,7 +130,6 @@ def __init__( session: OMCSessionZMQ, runpath: OMCPath, modelname: Optional[str] = None, - timeout: Optional[float] = None, ) -> None: if modelname is None: raise ModelicaSystemError("Missing model name!") @@ -138,7 +137,6 @@ def __init__( self._session = session self._runpath = runpath self._model_name = modelname - self._timeout = timeout # dictionaries of command line arguments for the model executable self._args: dict[str, str | None] = {} @@ -278,7 +276,6 @@ def definition(self) -> OMCSessionRunData: cmd_model_name=self._model_name, cmd_args=self.get_cmd_args(), cmd_result_path=result_file, - cmd_timeout=self._timeout, ) omc_run_data_updated = self._session.omc_run_data_update( @@ -337,7 +334,6 @@ def __init__( work_directory: Optional[str | os.PathLike] = None, omhome: Optional[str] = None, omc_process: Optional[OMCProcess] = None, - timeout: float = 10.0, ) -> None: """Create a ModelicaSystem instance. To define the model use model() or convertFmu2Mo(). @@ -351,7 +347,6 @@ def __init__( omhome: path to OMC to be used when creating the OMC session (see OMCSessionZMQ). omc_process: definition of a (local) OMC process to be used. If unspecified, a new local session will be created. - timeout: float value to define the timeout; if nothing is defined, a default value of 10s is used """ self._quantities: list[dict[str, Any]] = [] @@ -381,7 +376,7 @@ def __init__( if omc_process is not None: self._session = OMCSessionZMQ(omc_process=omc_process) else: - self._session = OMCSessionZMQ(omhome=omhome, timeout=timeout) + self._session = OMCSessionZMQ(omhome=omhome) # set commandLineOptions using default values or the user defined list if command_line_options is None: @@ -577,7 +572,6 @@ def buildModel(self, variableFilter: Optional[str] = None): session=self._session, runpath=self.getWorkDirectory(), modelname=self._model_name, - timeout=5.0, ) # ... by running it - output help for command help om_cmd.arg_set(key="help", val="help") @@ -1060,7 +1054,6 @@ def simulate_cmd( result_file: OMCPath, simflags: Optional[str] = None, simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None, - timeout: Optional[float] = None, ) -> ModelicaSystemCmd: """ This method prepares the simulates model according to the simulation options. It returns an instance of @@ -1077,7 +1070,6 @@ def simulate_cmd( result_file simflags simargs - timeout Returns ------- @@ -1088,7 +1080,6 @@ def simulate_cmd( session=self._session, runpath=self.getWorkDirectory(), modelname=self._model_name, - timeout=timeout, ) # always define the result file to use @@ -1138,7 +1129,6 @@ def simulate( resultfile: Optional[str | os.PathLike] = None, simflags: Optional[str] = None, simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None, - timeout: Optional[float] = None, ) -> None: """Simulate the model according to simulation options. @@ -1149,7 +1139,6 @@ def simulate( simflags: String of extra command line flags for the model binary. This argument is deprecated, use simargs instead. simargs: Dict with simulation runtime flags. - timeout: Maximum execution time in seconds. Examples: mod.simulate() @@ -1177,7 +1166,6 @@ def simulate( result_file=self._result_file, simflags=simflags, simargs=simargs, - timeout=timeout, ) # delete resultfile ... @@ -1761,7 +1749,6 @@ def linearize( lintime: Optional[float] = None, simflags: Optional[str] = None, simargs: Optional[dict[str, Optional[str | dict[str, Any] | numbers.Number]]] = None, - timeout: Optional[float] = None, ) -> LinearizationResult: """Linearize the model according to linearization options. @@ -1772,7 +1759,6 @@ def linearize( simflags: String of extra command line flags for the model binary. This argument is deprecated, use simargs instead. simargs: A dict with command line flags and possible options; example: "simargs={'csvInput': 'a.csv'}" - timeout: Maximum execution time in seconds. Returns: A LinearizationResult object is returned. This allows several @@ -1794,7 +1780,6 @@ def linearize( session=self._session, runpath=self.getWorkDirectory(), modelname=self._model_name, - timeout=timeout, ) override_content = ( @@ -1970,7 +1955,6 @@ def __init__( work_directory: Optional[str | os.PathLike] = None, omhome: Optional[str] = None, omc_process: Optional[OMCProcess] = None, - timeout: float = 10.0, # simulation specific input # TODO: add more settings (simulation options, input options, ...) simargs: Optional[dict[str, Optional[str | dict[str, str] | numbers.Number]]] = None, @@ -1991,7 +1975,6 @@ def __init__( work_directory=work_directory, omhome=omhome, omc_process=omc_process, - timeout=timeout, ) self._mod.model( model_file=model_file, @@ -2003,7 +1986,6 @@ def __init__( self._model_name = model_name self._simargs = simargs - self._timeout = timeout if resultpath is None: self._resultpath = self.session().omcpath_tempdir() @@ -2106,7 +2088,6 @@ def prepare(self) -> int: self._mod.setParameters(sim_param_non_structural) mscmd = self._mod.simulate_cmd( result_file=resultfile, - timeout=self._timeout, ) if self._simargs is not None: mscmd.args_set(args=self._simargs) From 582d76d7d865ea9c141a8d5b0d3cf32e2e41c7c1 Mon Sep 17 00:00:00 2001 From: syntron Date: Wed, 26 Nov 2025 20:21:33 +0100 Subject: [PATCH 3/7] [OMCSession*] define set_timeout() --- OMPython/OMCSession.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index 73d4b0c4..7062c39f 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -517,8 +517,6 @@ class OMCSessionRunData: cmd_model_executable: Optional[str] = None # additional library search path; this is mainly needed if OMCProcessLocal is run on Windows cmd_library_path: Optional[str] = None - # command timeout - cmd_timeout: Optional[float] = 10.0 # working directory to be used on the *local* system cmd_cwd_local: Optional[str] = None From 57efc8c09e04cff2c5809add5e6a2ea9c29b27f8 Mon Sep 17 00:00:00 2001 From: syntron Date: Tue, 25 Nov 2025 21:59:06 +0100 Subject: [PATCH 4/7] [OMCSession*] define set_timeout() --- OMPython/OMCSession.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index 7062c39f..54972c6a 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -602,6 +602,17 @@ def __del__(self): self.omc_zmq = None + def set_timeout(self, timeout: Optional[float] = None) -> float: + """ + Set the timeout to be used for OMC communication (OMCSession). + """ + retval = self._timeout + if timeout is not None: + if timeout <= 0.0: + raise OMCSessionException(f"Invalid timeout value: {timeout}!") + self._timeout = timeout + return retval + @staticmethod def escape_str(value: str) -> str: """ @@ -661,11 +672,9 @@ def omc_run_data_update(self, omc_run_data: OMCSessionRunData) -> OMCSessionRunD """ return self.omc_process.omc_run_data_update(omc_run_data=omc_run_data) - @staticmethod - def run_model_executable(cmd_run_data: OMCSessionRunData) -> int: + def run_model_executable(self, cmd_run_data: OMCSessionRunData) -> int: """ - Run the command defined in cmd_run_data. This class is defined as static method such that there is no need to - keep instances of over classes around. + Run the command defined in cmd_run_data. """ my_env = os.environ.copy() @@ -682,7 +691,7 @@ def run_model_executable(cmd_run_data: OMCSessionRunData) -> int: text=True, env=my_env, cwd=cmd_run_data.cmd_cwd_local, - timeout=cmd_run_data.cmd_timeout, + timeout=self._timeout, check=True, ) stdout = cmdres.stdout.strip() From 079374c60e9f8276a2085daf0156e717fca99179 Mon Sep 17 00:00:00 2001 From: syntron Date: Tue, 25 Nov 2025 22:26:36 +0100 Subject: [PATCH 5/7] [OMCSession*] align all usages of timeout to the same structure --- OMPython/OMCSession.py | 96 +++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index 54972c6a..a02a28de 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -727,18 +727,21 @@ def sendExpression(self, command: str, parsed: bool = True) -> Any: logger.debug("sendExpression(%r, parsed=%r)", command, parsed) + MAX_RETRIES = 50 attempts = 0 - while True: + while attempts < MAX_RETRIES: + attempts += 1 + try: self.omc_zmq.send_string(str(command), flags=zmq.NOBLOCK) break except zmq.error.Again: pass - attempts += 1 - if attempts >= 50: - raise OMCSessionException(f"No connection with OMC (timeout={self._timeout}). " - f"Log-file says: \n{self.omc_process.get_log()}") - time.sleep(self._timeout / 50.0) + time.sleep(self._timeout / MAX_RETRIES) + else: + logger.error(f"Docker did not start. Log-file says:\n{self.omc_process.get_log()}") + raise OMCSessionException(f"No connection with OMC (timeout={self._timeout}).") + if command == "quit()": self.omc_zmq.close() self.omc_zmq = None @@ -1030,25 +1033,23 @@ def _omc_port_get(self) -> str: port = None # See if the omc server is running + MAX_RETRIES = 80 attempts = 0 - while True: - omc_portfile_path = self._get_portfile_path() + while attempts < MAX_RETRIES: + attempts += 1 + omc_portfile_path = self._get_portfile_path() if omc_portfile_path is not None and omc_portfile_path.is_file(): # Read the port file with open(file=omc_portfile_path, mode='r', encoding="utf-8") as f_p: port = f_p.readline() break - if port is not None: break - - attempts += 1 - if attempts == 80.0: - raise OMCSessionException(f"OMC Server did not start (timeout={self._timeout}). " - f"Could not open file {omc_portfile_path}. " - f"Log-file says:\n{self.get_log()}") - time.sleep(self._timeout / 80.0) + time.sleep(self._timeout / MAX_RETRIES) + else: + logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}") + raise OMCSessionException(f"OMC Server did not start (timeout={self._timeout}).") logger.info(f"Local OMC Server is up and running at ZMQ port {port} " f"pid={self._omc_process.pid if isinstance(self._omc_process, subprocess.Popen) else '?'}") @@ -1130,7 +1131,11 @@ def _docker_process_get(self, docker_cid: str) -> Optional[DockerPopen]: raise NotImplementedError("Docker not supported on win32!") docker_process = None - for _ in range(0, 40): + MAX_RETRIES = 40 + attempts = 0 + while attempts < MAX_RETRIES: + attempts += 1 + docker_top = subprocess.check_output(["docker", "top", docker_cid]).decode().strip() docker_process = None for line in docker_top.split("\n"): @@ -1141,10 +1146,12 @@ def _docker_process_get(self, docker_cid: str) -> Optional[DockerPopen]: except psutil.NoSuchProcess as ex: raise OMCSessionException(f"Could not find PID {docker_top} - " "is this a docker instance spawned without --pid=host?") from ex - if docker_process is not None: break - time.sleep(self._timeout / 40.0) + time.sleep(self._timeout / MAX_RETRIES) + else: + logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}") + raise OMCSessionException(f"Docker based OMC Server did not start (timeout={self._timeout}).") return docker_process @@ -1166,8 +1173,11 @@ def _omc_port_get(self) -> str: raise OMCSessionException(f"Invalid docker container ID: {self._docker_container_id}") # See if the omc server is running + MAX_RETRIES = 80 attempts = 0 - while True: + while attempts < MAX_RETRIES: + attempts += 1 + omc_portfile_path = self._get_portfile_path() if omc_portfile_path is not None: try: @@ -1178,16 +1188,12 @@ def _omc_port_get(self) -> str: port = output.decode().strip() except subprocess.CalledProcessError: pass - if port is not None: break - - attempts += 1 - if attempts == 80.0: - raise OMCSessionException(f"Docker based OMC Server did not start (timeout={self._timeout}). " - f"Could not open port file {omc_portfile_path}. " - f"Log-file says:\n{self.get_log()}") - time.sleep(self._timeout / 80.0) + time.sleep(self._timeout / MAX_RETRIES) + else: + logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}") + raise OMCSessionException(f"Docker based OMC Server did not start (timeout={self._timeout}).") logger.info(f"Docker based OMC Server is up and running at port {port}") @@ -1355,25 +1361,28 @@ def _docker_omc_start(self) -> Tuple[subprocess.Popen, DockerPopen, str]: raise OMCSessionException(f"Invalid content for docker container ID file path: {docker_cid_file}") docker_cid = None - for _ in range(0, 40): + MAX_RETRIES = 40 + attempts = 0 + while attempts < MAX_RETRIES: + attempts += 1 + try: with open(file=docker_cid_file, mode="r", encoding="utf-8") as fh: docker_cid = fh.read().strip() except IOError: pass - if docker_cid: + if docker_cid is not None: break - time.sleep(self._timeout / 40.0) - - if docker_cid is None: + time.sleep(self._timeout / MAX_RETRIES) + else: logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}") raise OMCSessionException(f"Docker did not start (timeout={self._timeout} might be too short " "especially if you did not docker pull the image before this command).") docker_process = self._docker_process_get(docker_cid=docker_cid) if docker_process is None: - raise OMCSessionException(f"Docker top did not contain omc process {self._random_string}. " - f"Log-file says:\n{self.get_log()}") + logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}") + raise OMCSessionException(f"Docker top did not contain omc process {self._random_string}.") return omc_process, docker_process, docker_cid @@ -1529,8 +1538,11 @@ def _omc_port_get(self) -> str: port = None # See if the omc server is running + MAX_RETRIES = 80 attempts = 0 - while True: + while attempts < MAX_RETRIES: + attempts += 1 + try: omc_portfile_path = self._get_portfile_path() if omc_portfile_path is not None: @@ -1541,16 +1553,12 @@ def _omc_port_get(self) -> str: port = output.decode().strip() except subprocess.CalledProcessError: pass - if port is not None: break - - attempts += 1 - if attempts == 80.0: - raise OMCSessionException(f"WSL based OMC Server did not start (timeout={self._timeout}). " - f"Could not open port file {omc_portfile_path}. " - f"Log-file says:\n{self.get_log()}") - time.sleep(self._timeout / 80.0) + time.sleep(self._timeout / MAX_RETRIES) + else: + logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}") + raise OMCSessionException(f"WSL based OMC Server did not start (timeout={self._timeout}).") logger.info(f"WSL based OMC Server is up and running at ZMQ port {port} " f"pid={self._omc_process.pid if isinstance(self._omc_process, subprocess.Popen) else '?'}") From 1387ec452b48cb2c6dd5c4f51d89ffdfe9dacf11 Mon Sep 17 00:00:00 2001 From: syntron Date: Wed, 26 Nov 2025 19:38:48 +0100 Subject: [PATCH 6/7] [OMCSession*] simplify code for timeout loops --- OMPython/OMCSession.py | 73 +++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index a02a28de..e3e3b4c1 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -727,17 +727,13 @@ def sendExpression(self, command: str, parsed: bool = True) -> Any: logger.debug("sendExpression(%r, parsed=%r)", command, parsed) - MAX_RETRIES = 50 - attempts = 0 - while attempts < MAX_RETRIES: - attempts += 1 - + loop = self.omc_process._timeout_loop(timestep=0.05) + while next(loop): try: self.omc_zmq.send_string(str(command), flags=zmq.NOBLOCK) break except zmq.error.Again: pass - time.sleep(self._timeout / MAX_RETRIES) else: logger.error(f"Docker did not start. Log-file says:\n{self.omc_process.get_log()}") raise OMCSessionException(f"No connection with OMC (timeout={self._timeout}).") @@ -912,6 +908,31 @@ def __del__(self): finally: self._omc_process = None + def _timeout_loop( + self, + timeout: Optional[float] = None, + timestep: float = 0.1, + ): + """ + Helper (using yield) for while loops to check OMC startup / response. The loop is executed as long as True is + returned, i.e. the first False will stop the while loop. + """ + + if timeout is None: + timeout = self._timeout + if timeout <= 0: + raise OMCSessionException(f"Invalid timeout: {timeout}") + + timer = 0 + yield True + while True: + timer += timestep + if timer > timeout: + break + time.sleep(timestep) + yield True + yield False + def get_port(self) -> Optional[str]: """ Get the port to connect to the OMC process. @@ -1033,11 +1054,8 @@ def _omc_port_get(self) -> str: port = None # See if the omc server is running - MAX_RETRIES = 80 - attempts = 0 - while attempts < MAX_RETRIES: - attempts += 1 - + loop = self._timeout_loop(timestep=0.1) + while next(loop): omc_portfile_path = self._get_portfile_path() if omc_portfile_path is not None and omc_portfile_path.is_file(): # Read the port file @@ -1046,7 +1064,6 @@ def _omc_port_get(self) -> str: break if port is not None: break - time.sleep(self._timeout / MAX_RETRIES) else: logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}") raise OMCSessionException(f"OMC Server did not start (timeout={self._timeout}).") @@ -1131,11 +1148,8 @@ def _docker_process_get(self, docker_cid: str) -> Optional[DockerPopen]: raise NotImplementedError("Docker not supported on win32!") docker_process = None - MAX_RETRIES = 40 - attempts = 0 - while attempts < MAX_RETRIES: - attempts += 1 - + loop = self._timeout_loop(timestep=0.2) + while next(loop): docker_top = subprocess.check_output(["docker", "top", docker_cid]).decode().strip() docker_process = None for line in docker_top.split("\n"): @@ -1148,7 +1162,6 @@ def _docker_process_get(self, docker_cid: str) -> Optional[DockerPopen]: "is this a docker instance spawned without --pid=host?") from ex if docker_process is not None: break - time.sleep(self._timeout / MAX_RETRIES) else: logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}") raise OMCSessionException(f"Docker based OMC Server did not start (timeout={self._timeout}).") @@ -1173,11 +1186,8 @@ def _omc_port_get(self) -> str: raise OMCSessionException(f"Invalid docker container ID: {self._docker_container_id}") # See if the omc server is running - MAX_RETRIES = 80 - attempts = 0 - while attempts < MAX_RETRIES: - attempts += 1 - + loop = self._timeout_loop(timestep=0.1) + while next(loop): omc_portfile_path = self._get_portfile_path() if omc_portfile_path is not None: try: @@ -1190,7 +1200,6 @@ def _omc_port_get(self) -> str: pass if port is not None: break - time.sleep(self._timeout / MAX_RETRIES) else: logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}") raise OMCSessionException(f"Docker based OMC Server did not start (timeout={self._timeout}).") @@ -1361,11 +1370,8 @@ def _docker_omc_start(self) -> Tuple[subprocess.Popen, DockerPopen, str]: raise OMCSessionException(f"Invalid content for docker container ID file path: {docker_cid_file}") docker_cid = None - MAX_RETRIES = 40 - attempts = 0 - while attempts < MAX_RETRIES: - attempts += 1 - + loop = self._timeout_loop(timestep=0.1) + while next(loop): try: with open(file=docker_cid_file, mode="r", encoding="utf-8") as fh: docker_cid = fh.read().strip() @@ -1373,7 +1379,6 @@ def _docker_omc_start(self) -> Tuple[subprocess.Popen, DockerPopen, str]: pass if docker_cid is not None: break - time.sleep(self._timeout / MAX_RETRIES) else: logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}") raise OMCSessionException(f"Docker did not start (timeout={self._timeout} might be too short " @@ -1538,11 +1543,8 @@ def _omc_port_get(self) -> str: port = None # See if the omc server is running - MAX_RETRIES = 80 - attempts = 0 - while attempts < MAX_RETRIES: - attempts += 1 - + loop = self._timeout_loop(timestep=0.1) + while next(loop): try: omc_portfile_path = self._get_portfile_path() if omc_portfile_path is not None: @@ -1555,7 +1557,6 @@ def _omc_port_get(self) -> str: pass if port is not None: break - time.sleep(self._timeout / MAX_RETRIES) else: logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}") raise OMCSessionException(f"WSL based OMC Server did not start (timeout={self._timeout}).") From f9d995c9f367f3110328631af7438673e1b98641 Mon Sep 17 00:00:00 2001 From: syntron Date: Wed, 26 Nov 2025 19:43:48 +0100 Subject: [PATCH 7/7] [OMCSession*] fix variable type to float --- OMPython/OMCSession.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OMPython/OMCSession.py b/OMPython/OMCSession.py index e3e3b4c1..757a08a1 100644 --- a/OMPython/OMCSession.py +++ b/OMPython/OMCSession.py @@ -923,7 +923,7 @@ def _timeout_loop( if timeout <= 0: raise OMCSessionException(f"Invalid timeout: {timeout}") - timer = 0 + timer = 0.0 yield True while True: timer += timestep