@@ -517,8 +517,6 @@ class OMCSessionRunData:
517517 cmd_model_executable : Optional [str ] = None
518518 # additional library search path; this is mainly needed if OMCProcessLocal is run on Windows
519519 cmd_library_path : Optional [str ] = None
520- # command timeout
521- cmd_timeout : Optional [float ] = 10.0
522520
523521 # working directory to be used on the *local* system
524522 cmd_cwd_local : Optional [str ] = None
@@ -593,13 +591,12 @@ def omc_run_data_update(self, omc_run_data: OMCSessionRunData) -> OMCSessionRunD
593591 """
594592 return self .omc_process .omc_run_data_update (omc_run_data = omc_run_data )
595593
596- @staticmethod
597- def run_model_executable (cmd_run_data : OMCSessionRunData ) -> int :
594+ def run_model_executable (self , cmd_run_data : OMCSessionRunData ) -> int :
598595 """
599596 Run the command defined in cmd_run_data. This class is defined as static method such that there is no need to
600597 keep instances of over classes around.
601598 """
602- return OMCSession .run_model_executable (cmd_run_data = cmd_run_data )
599+ return self . omc_process .run_model_executable (cmd_run_data = cmd_run_data )
603600
604601 def execute (self , command : str ):
605602 return self .omc_process .execute (command = command )
@@ -714,6 +711,9 @@ def __post_init__(self) -> None:
714711 """
715712 Create the connection to the OMC server using ZeroMQ.
716713 """
714+ # set_timeout() is used to define the value of _timeout as it includes additional checks
715+ self .set_timeout (timeout = self ._timeout )
716+
717717 port = self .get_port ()
718718 if not isinstance (port , str ):
719719 raise OMCSessionException (f"Invalid content for port: { port } " )
@@ -756,6 +756,44 @@ def __del__(self):
756756 finally :
757757 self ._omc_process = None
758758
759+ def _timeout_loop (
760+ self ,
761+ timeout : Optional [float ] = None ,
762+ timestep : float = 0.1 ,
763+ ):
764+ """
765+ Helper (using yield) for while loops to check OMC startup / response. The loop is executed as long as True is
766+ returned, i.e. the first False will stop the while loop.
767+ """
768+
769+ if timeout is None :
770+ timeout = self ._timeout
771+ if timeout <= 0 :
772+ raise OMCSessionException (f"Invalid timeout: { timeout } " )
773+
774+ timer = 0.0
775+ yield True
776+ while True :
777+ timer += timestep
778+ if timer > timeout :
779+ break
780+ time .sleep (timestep )
781+ yield True
782+ yield False
783+
784+ def set_timeout (self , timeout : Optional [float ] = None ) -> float :
785+ """
786+ Set the timeout to be used for OMC communication (OMCSession).
787+
788+ The defined value is set and the current value is returned. If None is provided as argument, nothing is changed.
789+ """
790+ retval = self ._timeout
791+ if timeout is not None :
792+ if timeout <= 0.0 :
793+ raise OMCSessionException (f"Invalid timeout value: { timeout } !" )
794+ self ._timeout = timeout
795+ return retval
796+
759797 @staticmethod
760798 def escape_str (value : str ) -> str :
761799 """
@@ -807,11 +845,9 @@ def omcpath_tempdir(self, tempdir_base: Optional[OMCPath] = None) -> OMCPath:
807845
808846 return tempdir
809847
810- @staticmethod
811- def run_model_executable (cmd_run_data : OMCSessionRunData ) -> int :
848+ def run_model_executable (self , cmd_run_data : OMCSessionRunData ) -> int :
812849 """
813- Run the command defined in cmd_run_data. This class is defined as static method such that there is no need to
814- keep instances of over classes around.
850+ Run the command defined in cmd_run_data.
815851 """
816852
817853 my_env = os .environ .copy ()
@@ -828,7 +864,7 @@ def run_model_executable(cmd_run_data: OMCSessionRunData) -> int:
828864 text = True ,
829865 env = my_env ,
830866 cwd = cmd_run_data .cmd_cwd_local ,
831- timeout = cmd_run_data . cmd_timeout ,
867+ timeout = self . _timeout ,
832868 check = True ,
833869 )
834870 stdout = cmdres .stdout .strip ()
@@ -862,34 +898,28 @@ def sendExpression(self, command: str, parsed: bool = True) -> Any:
862898 Caller should only check for OMCSessionException.
863899 """
864900
865- # this is needed if the class is not fully initialized or in the process of deletion
866- if hasattr (self , '_timeout' ):
867- timeout = self ._timeout
868- else :
869- timeout = 1.0
870-
871901 if self ._omc_zmq is None :
872902 raise OMCSessionException ("No OMC running. Please create a new instance of OMCSession!" )
873903
874904 logger .debug ("sendExpression(%r, parsed=%r)" , command , parsed )
875905
876- attempts = 0
877- while True :
906+ loop = self . _timeout_loop ( timestep = 0.05 )
907+ while next ( loop ) :
878908 try :
879909 self ._omc_zmq .send_string (str (command ), flags = zmq .NOBLOCK )
880910 break
881911 except zmq .error .Again :
882912 pass
883- attempts += 1
884- if attempts >= 50 :
885- # in the deletion process, the content is cleared. Thus, any access to a class attribute must be checked
886- try :
887- log_content = self . get_log ()
888- except OMCSessionException :
889- log_content = 'log not available'
890- raise OMCSessionException (f"No connection with OMC (timeout= { timeout } ). "
891- f"Log-file says: \n { log_content } " )
892- time . sleep ( timeout / 50.0 )
913+ else :
914+ # in the deletion process, the content is cleared. Thus, any access to a class attribute must be checked
915+ try :
916+ log_content = self . get_log ()
917+ except OMCSessionException :
918+ log_content = 'log not available'
919+
920+ logger . error (f"Docker did not start. Log-file says: \n { log_content } " )
921+ raise OMCSessionException ( f"No connection with OMC (timeout= { self . _timeout } ). " )
922+
893923 if command == "quit()" :
894924 self ._omc_zmq .close ()
895925 self ._omc_zmq = None
@@ -985,7 +1015,7 @@ def sendExpression(self, command: str, parsed: bool = True) -> Any:
9851015 raise OMCSessionException (f"OMC error occurred for 'sendExpression({ command } , { parsed } ):\n "
9861016 f"{ msg_long_str } " )
9871017
988- if parsed is False :
1018+ if not parsed :
9891019 return result
9901020
9911021 try :
@@ -1134,25 +1164,19 @@ def _omc_port_get(self) -> str:
11341164 port = None
11351165
11361166 # See if the omc server is running
1137- attempts = 0
1138- while True :
1167+ loop = self . _timeout_loop ( timestep = 0.1 )
1168+ while next ( loop ) :
11391169 omc_portfile_path = self ._get_portfile_path ()
1140-
11411170 if omc_portfile_path is not None and omc_portfile_path .is_file ():
11421171 # Read the port file
11431172 with open (file = omc_portfile_path , mode = 'r' , encoding = "utf-8" ) as f_p :
11441173 port = f_p .readline ()
11451174 break
1146-
11471175 if port is not None :
11481176 break
1149-
1150- attempts += 1
1151- if attempts == 80.0 :
1152- raise OMCSessionException (f"OMC Server did not start (timeout={ self ._timeout } ). "
1153- f"Could not open file { omc_portfile_path } . "
1154- f"Log-file says:\n { self .get_log ()} " )
1155- time .sleep (self ._timeout / 80.0 )
1177+ else :
1178+ logger .error (f"Docker did not start. Log-file says:\n { self .get_log ()} " )
1179+ raise OMCSessionException (f"OMC Server did not start (timeout={ self ._timeout } )." )
11561180
11571181 logger .info (f"Local OMC Server is up and running at ZMQ port { port } "
11581182 f"pid={ self ._omc_process .pid if isinstance (self ._omc_process , subprocess .Popen ) else '?' } " )
@@ -1233,8 +1257,8 @@ def _docker_process_get(self, docker_cid: str) -> Optional[DockerPopen]:
12331257 if sys .platform == 'win32' :
12341258 raise NotImplementedError ("Docker not supported on win32!" )
12351259
1236- docker_process = None
1237- for _ in range ( 0 , 40 ):
1260+ loop = self . _timeout_loop ( timestep = 0.2 )
1261+ while next ( loop ):
12381262 docker_top = subprocess .check_output (["docker" , "top" , docker_cid ]).decode ().strip ()
12391263 docker_process = None
12401264 for line in docker_top .split ("\n " ):
@@ -1245,10 +1269,11 @@ def _docker_process_get(self, docker_cid: str) -> Optional[DockerPopen]:
12451269 except psutil .NoSuchProcess as ex :
12461270 raise OMCSessionException (f"Could not find PID { docker_top } - "
12471271 "is this a docker instance spawned without --pid=host?" ) from ex
1248-
12491272 if docker_process is not None :
12501273 break
1251- time .sleep (self ._timeout / 40.0 )
1274+ else :
1275+ logger .error (f"Docker did not start. Log-file says:\n { self .get_log ()} " )
1276+ raise OMCSessionException (f"Docker based OMC Server did not start (timeout={ self ._timeout } )." )
12521277
12531278 return docker_process
12541279
@@ -1270,8 +1295,8 @@ def _omc_port_get(self) -> str:
12701295 raise OMCSessionException (f"Invalid docker container ID: { self ._docker_container_id } " )
12711296
12721297 # See if the omc server is running
1273- attempts = 0
1274- while True :
1298+ loop = self . _timeout_loop ( timestep = 0.1 )
1299+ while next ( loop ) :
12751300 omc_portfile_path = self ._get_portfile_path ()
12761301 if omc_portfile_path is not None :
12771302 try :
@@ -1282,16 +1307,11 @@ def _omc_port_get(self) -> str:
12821307 port = output .decode ().strip ()
12831308 except subprocess .CalledProcessError :
12841309 pass
1285-
12861310 if port is not None :
12871311 break
1288-
1289- attempts += 1
1290- if attempts == 80.0 :
1291- raise OMCSessionException (f"Docker based OMC Server did not start (timeout={ self ._timeout } ). "
1292- f"Could not open port file { omc_portfile_path } . "
1293- f"Log-file says:\n { self .get_log ()} " )
1294- time .sleep (self ._timeout / 80.0 )
1312+ else :
1313+ logger .error (f"Docker did not start. Log-file says:\n { self .get_log ()} " )
1314+ raise OMCSessionException (f"Docker based OMC Server did not start (timeout={ self ._timeout } )." )
12951315
12961316 logger .info (f"Docker based OMC Server is up and running at port { port } " )
12971317
@@ -1459,25 +1479,24 @@ def _docker_omc_start(self) -> Tuple[subprocess.Popen, DockerPopen, str]:
14591479 raise OMCSessionException (f"Invalid content for docker container ID file path: { docker_cid_file } " )
14601480
14611481 docker_cid = None
1462- for _ in range (0 , 40 ):
1482+ loop = self ._timeout_loop (timestep = 0.1 )
1483+ while next (loop ):
14631484 try :
14641485 with open (file = docker_cid_file , mode = "r" , encoding = "utf-8" ) as fh :
14651486 docker_cid = fh .read ().strip ()
14661487 except IOError :
14671488 pass
1468- if docker_cid :
1489+ if docker_cid is not None :
14691490 break
1470- time .sleep (self ._timeout / 40.0 )
1471-
1472- if docker_cid is None :
1491+ else :
14731492 logger .error (f"Docker did not start. Log-file says:\n { self .get_log ()} " )
14741493 raise OMCSessionException (f"Docker did not start (timeout={ self ._timeout } might be too short "
14751494 "especially if you did not docker pull the image before this command)." )
14761495
14771496 docker_process = self ._docker_process_get (docker_cid = docker_cid )
14781497 if docker_process is None :
1479- raise OMCSessionException (f"Docker top did not contain omc process { self ._random_string } . "
1480- f"Log-file says: \n { self .get_log () } " )
1498+ logger . error (f"Docker did not start. Log-file says: \n { self .get_log () } " )
1499+ raise OMCSessionException ( f"Docker top did not contain omc process { self ._random_string } . " )
14811500
14821501 return omc_process , docker_process , docker_cid
14831502
@@ -1629,12 +1648,11 @@ def _omc_process_get(self) -> subprocess.Popen:
16291648 return omc_process
16301649
16311650 def _omc_port_get (self ) -> str :
1632- omc_portfile_path : Optional [pathlib .Path ] = None
16331651 port = None
16341652
16351653 # See if the omc server is running
1636- attempts = 0
1637- while True :
1654+ loop = self . _timeout_loop ( timestep = 0.1 )
1655+ while next ( loop ) :
16381656 try :
16391657 omc_portfile_path = self ._get_portfile_path ()
16401658 if omc_portfile_path is not None :
@@ -1645,16 +1663,11 @@ def _omc_port_get(self) -> str:
16451663 port = output .decode ().strip ()
16461664 except subprocess .CalledProcessError :
16471665 pass
1648-
16491666 if port is not None :
16501667 break
1651-
1652- attempts += 1
1653- if attempts == 80.0 :
1654- raise OMCSessionException (f"WSL based OMC Server did not start (timeout={ self ._timeout } ). "
1655- f"Could not open port file { omc_portfile_path } . "
1656- f"Log-file says:\n { self .get_log ()} " )
1657- time .sleep (self ._timeout / 80.0 )
1668+ else :
1669+ logger .error (f"Docker did not start. Log-file says:\n { self .get_log ()} " )
1670+ raise OMCSessionException (f"WSL based OMC Server did not start (timeout={ self ._timeout } )." )
16581671
16591672 logger .info (f"WSL based OMC Server is up and running at ZMQ port { port } "
16601673 f"pid={ self ._omc_process .pid if isinstance (self ._omc_process , subprocess .Popen ) else '?' } " )
0 commit comments