@@ -482,8 +482,6 @@ class OMCSessionRunData:
482482 cmd_model_executable : Optional [str ] = None
483483 # additional library search path; this is mainly needed if OMCProcessLocal is run on Windows
484484 cmd_library_path : Optional [str ] = None
485- # command timeout
486- cmd_timeout : Optional [float ] = 10.0
487485
488486 # working directory to be used on the *local* system
489487 cmd_cwd_local : Optional [str ] = None
@@ -558,13 +556,12 @@ def omc_run_data_update(self, omc_run_data: OMCSessionRunData) -> OMCSessionRunD
558556 """
559557 return self .omc_process .omc_run_data_update (omc_run_data = omc_run_data )
560558
561- @staticmethod
562- def run_model_executable (cmd_run_data : OMCSessionRunData ) -> int :
559+ def run_model_executable (self , cmd_run_data : OMCSessionRunData ) -> int :
563560 """
564561 Run the command defined in cmd_run_data. This class is defined as static method such that there is no need to
565562 keep instances of over classes around.
566563 """
567- return OMCSession .run_model_executable (cmd_run_data = cmd_run_data )
564+ return self . omc_process .run_model_executable (cmd_run_data = cmd_run_data )
568565
569566 def execute (self , command : str ):
570567 return self .omc_process .execute (command = command )
@@ -679,6 +676,9 @@ def __post_init__(self) -> None:
679676 """
680677 Create the connection to the OMC server using ZeroMQ.
681678 """
679+ # set_timeout() is used to define the value of _timeout as it includes additional checks
680+ self .set_timeout (timeout = self ._timeout )
681+
682682 port = self .get_port ()
683683 if not isinstance (port , str ):
684684 raise OMCSessionException (f"Invalid content for port: { port } " )
@@ -721,6 +721,44 @@ def __del__(self):
721721 finally :
722722 self ._omc_process = None
723723
724+ def _timeout_loop (
725+ self ,
726+ timeout : Optional [float ] = None ,
727+ timestep : float = 0.1 ,
728+ ):
729+ """
730+ Helper (using yield) for while loops to check OMC startup / response. The loop is executed as long as True is
731+ returned, i.e. the first False will stop the while loop.
732+ """
733+
734+ if timeout is None :
735+ timeout = self ._timeout
736+ if timeout <= 0 :
737+ raise OMCSessionException (f"Invalid timeout: { timeout } " )
738+
739+ timer = 0.0
740+ yield True
741+ while True :
742+ timer += timestep
743+ if timer > timeout :
744+ break
745+ time .sleep (timestep )
746+ yield True
747+ yield False
748+
749+ def set_timeout (self , timeout : Optional [float ] = None ) -> float :
750+ """
751+ Set the timeout to be used for OMC communication (OMCSession).
752+
753+ The defined value is set and the current value is returned. If None is provided as argument, nothing is changed.
754+ """
755+ retval = self ._timeout
756+ if timeout is not None :
757+ if timeout <= 0.0 :
758+ raise OMCSessionException (f"Invalid timeout value: { timeout } !" )
759+ self ._timeout = timeout
760+ return retval
761+
724762 @staticmethod
725763 def escape_str (value : str ) -> str :
726764 """
@@ -772,11 +810,9 @@ def omcpath_tempdir(self, tempdir_base: Optional[OMCPath] = None) -> OMCPath:
772810
773811 return tempdir
774812
775- @staticmethod
776- def run_model_executable (cmd_run_data : OMCSessionRunData ) -> int :
813+ def run_model_executable (self , cmd_run_data : OMCSessionRunData ) -> int :
777814 """
778- Run the command defined in cmd_run_data. This class is defined as static method such that there is no need to
779- keep instances of over classes around.
815+ Run the command defined in cmd_run_data.
780816 """
781817
782818 my_env = os .environ .copy ()
@@ -793,8 +829,8 @@ def run_model_executable(cmd_run_data: OMCSessionRunData) -> int:
793829 text = True ,
794830 env = my_env ,
795831 cwd = cmd_run_data .cmd_cwd_local ,
796- timeout = cmd_run_data . cmd_timeout ,
797- check = False ,
832+ timeout = self . _timeout ,
833+ check = True ,
798834 )
799835 stdout = cmdres .stdout .strip ()
800836 stderr = cmdres .stderr .strip ()
@@ -830,34 +866,28 @@ def sendExpression(self, command: str, parsed: bool = True) -> Any:
830866 Caller should only check for OMCSessionException.
831867 """
832868
833- # this is needed if the class is not fully initialized or in the process of deletion
834- if hasattr (self , '_timeout' ):
835- timeout = self ._timeout
836- else :
837- timeout = 1.0
838-
839869 if self ._omc_zmq is None :
840870 raise OMCSessionException ("No OMC running. Please create a new instance of OMCSession!" )
841871
842872 logger .debug ("sendExpression(%r, parsed=%r)" , command , parsed )
843873
844- attempts = 0
845- while True :
874+ loop = self . _timeout_loop ( timestep = 0.05 )
875+ while next ( loop ) :
846876 try :
847877 self ._omc_zmq .send_string (str (command ), flags = zmq .NOBLOCK )
848878 break
849879 except zmq .error .Again :
850880 pass
851- attempts += 1
852- if attempts >= 50 :
853- # in the deletion process, the content is cleared. Thus, any access to a class attribute must be checked
854- try :
855- log_content = self . get_log ()
856- except OMCSessionException :
857- log_content = 'log not available'
858- raise OMCSessionException (f"No connection with OMC (timeout= { timeout } ). "
859- f"Log-file says: \n { log_content } " )
860- time . sleep ( timeout / 50.0 )
881+ else :
882+ # in the deletion process, the content is cleared. Thus, any access to a class attribute must be checked
883+ try :
884+ log_content = self . get_log ()
885+ except OMCSessionException :
886+ log_content = 'log not available'
887+
888+ logger . error (f"OMC did not start. Log-file says: \n { log_content } " )
889+ raise OMCSessionException ( f"No connection with OMC (timeout= { self . _timeout } ). " )
890+
861891 if command == "quit()" :
862892 self ._omc_zmq .close ()
863893 self ._omc_zmq = None
@@ -953,7 +983,7 @@ def sendExpression(self, command: str, parsed: bool = True) -> Any:
953983 raise OMCSessionException (f"OMC error occurred for 'sendExpression({ command } , { parsed } ):\n "
954984 f"{ msg_long_str } " )
955985
956- if parsed is False :
986+ if not parsed :
957987 return result
958988
959989 try :
@@ -1102,25 +1132,19 @@ def _omc_port_get(self) -> str:
11021132 port = None
11031133
11041134 # See if the omc server is running
1105- attempts = 0
1106- while True :
1135+ loop = self . _timeout_loop ( timestep = 0.1 )
1136+ while next ( loop ) :
11071137 omc_portfile_path = self ._get_portfile_path ()
1108-
11091138 if omc_portfile_path is not None and omc_portfile_path .is_file ():
11101139 # Read the port file
11111140 with open (file = omc_portfile_path , mode = 'r' , encoding = "utf-8" ) as f_p :
11121141 port = f_p .readline ()
11131142 break
1114-
11151143 if port is not None :
11161144 break
1117-
1118- attempts += 1
1119- if attempts == 80.0 :
1120- raise OMCSessionException (f"OMC Server did not start (timeout={ self ._timeout } ). "
1121- f"Could not open file { omc_portfile_path } . "
1122- f"Log-file says:\n { self .get_log ()} " )
1123- time .sleep (self ._timeout / 80.0 )
1145+ else :
1146+ logger .error (f"Docker did not start. Log-file says:\n { self .get_log ()} " )
1147+ raise OMCSessionException (f"OMC Server did not start (timeout={ self ._timeout } )." )
11241148
11251149 logger .info (f"Local OMC Server is up and running at ZMQ port { port } "
11261150 f"pid={ self ._omc_process .pid if isinstance (self ._omc_process , subprocess .Popen ) else '?' } " )
@@ -1201,8 +1225,8 @@ def _docker_process_get(self, docker_cid: str) -> Optional[DockerPopen]:
12011225 if sys .platform == 'win32' :
12021226 raise NotImplementedError ("Docker not supported on win32!" )
12031227
1204- docker_process = None
1205- for _ in range ( 0 , 40 ):
1228+ loop = self . _timeout_loop ( timestep = 0.2 )
1229+ while next ( loop ):
12061230 docker_top = subprocess .check_output (["docker" , "top" , docker_cid ]).decode ().strip ()
12071231 docker_process = None
12081232 for line in docker_top .split ("\n " ):
@@ -1213,10 +1237,11 @@ def _docker_process_get(self, docker_cid: str) -> Optional[DockerPopen]:
12131237 except psutil .NoSuchProcess as ex :
12141238 raise OMCSessionException (f"Could not find PID { docker_top } - "
12151239 "is this a docker instance spawned without --pid=host?" ) from ex
1216-
12171240 if docker_process is not None :
12181241 break
1219- time .sleep (self ._timeout / 40.0 )
1242+ else :
1243+ logger .error (f"Docker did not start. Log-file says:\n { self .get_log ()} " )
1244+ raise OMCSessionException (f"Docker based OMC Server did not start (timeout={ self ._timeout } )." )
12201245
12211246 return docker_process
12221247
@@ -1238,8 +1263,8 @@ def _omc_port_get(self) -> str:
12381263 raise OMCSessionException (f"Invalid docker container ID: { self ._docker_container_id } " )
12391264
12401265 # See if the omc server is running
1241- attempts = 0
1242- while True :
1266+ loop = self . _timeout_loop ( timestep = 0.1 )
1267+ while next ( loop ) :
12431268 omc_portfile_path = self ._get_portfile_path ()
12441269 if omc_portfile_path is not None :
12451270 try :
@@ -1250,16 +1275,11 @@ def _omc_port_get(self) -> str:
12501275 port = output .decode ().strip ()
12511276 except subprocess .CalledProcessError :
12521277 pass
1253-
12541278 if port is not None :
12551279 break
1256-
1257- attempts += 1
1258- if attempts == 80.0 :
1259- raise OMCSessionException (f"Docker based OMC Server did not start (timeout={ self ._timeout } ). "
1260- f"Could not open port file { omc_portfile_path } . "
1261- f"Log-file says:\n { self .get_log ()} " )
1262- time .sleep (self ._timeout / 80.0 )
1280+ else :
1281+ logger .error (f"Docker did not start. Log-file says:\n { self .get_log ()} " )
1282+ raise OMCSessionException (f"Docker based OMC Server did not start (timeout={ self ._timeout } )." )
12631283
12641284 logger .info (f"Docker based OMC Server is up and running at port { port } " )
12651285
@@ -1427,25 +1447,24 @@ def _docker_omc_start(self) -> Tuple[subprocess.Popen, DockerPopen, str]:
14271447 raise OMCSessionException (f"Invalid content for docker container ID file path: { docker_cid_file } " )
14281448
14291449 docker_cid = None
1430- for _ in range (0 , 40 ):
1450+ loop = self ._timeout_loop (timestep = 0.1 )
1451+ while next (loop ):
14311452 try :
14321453 with open (file = docker_cid_file , mode = "r" , encoding = "utf-8" ) as fh :
14331454 docker_cid = fh .read ().strip ()
14341455 except IOError :
14351456 pass
1436- if docker_cid :
1457+ if docker_cid is not None :
14371458 break
1438- time .sleep (self ._timeout / 40.0 )
1439-
1440- if docker_cid is None :
1459+ else :
14411460 logger .error (f"Docker did not start. Log-file says:\n { self .get_log ()} " )
14421461 raise OMCSessionException (f"Docker did not start (timeout={ self ._timeout } might be too short "
14431462 "especially if you did not docker pull the image before this command)." )
14441463
14451464 docker_process = self ._docker_process_get (docker_cid = docker_cid )
14461465 if docker_process is None :
1447- raise OMCSessionException (f"Docker top did not contain omc process { self ._random_string } . "
1448- f"Log-file says: \n { self .get_log () } " )
1466+ logger . error (f"Docker did not start. Log-file says: \n { self .get_log () } " )
1467+ raise OMCSessionException ( f"Docker top did not contain omc process { self ._random_string } . " )
14491468
14501469 return omc_process , docker_process , docker_cid
14511470
@@ -1597,12 +1616,11 @@ def _omc_process_get(self) -> subprocess.Popen:
15971616 return omc_process
15981617
15991618 def _omc_port_get (self ) -> str :
1600- omc_portfile_path : Optional [pathlib .Path ] = None
16011619 port = None
16021620
16031621 # See if the omc server is running
1604- attempts = 0
1605- while True :
1622+ loop = self . _timeout_loop ( timestep = 0.1 )
1623+ while next ( loop ) :
16061624 try :
16071625 omc_portfile_path = self ._get_portfile_path ()
16081626 if omc_portfile_path is not None :
@@ -1613,16 +1631,11 @@ def _omc_port_get(self) -> str:
16131631 port = output .decode ().strip ()
16141632 except subprocess .CalledProcessError :
16151633 pass
1616-
16171634 if port is not None :
16181635 break
1619-
1620- attempts += 1
1621- if attempts == 80.0 :
1622- raise OMCSessionException (f"WSL based OMC Server did not start (timeout={ self ._timeout } ). "
1623- f"Could not open port file { omc_portfile_path } . "
1624- f"Log-file says:\n { self .get_log ()} " )
1625- time .sleep (self ._timeout / 80.0 )
1636+ else :
1637+ logger .error (f"Docker did not start. Log-file says:\n { self .get_log ()} " )
1638+ raise OMCSessionException (f"WSL based OMC Server did not start (timeout={ self ._timeout } )." )
16261639
16271640 logger .info (f"WSL based OMC Server is up and running at ZMQ port { port } "
16281641 f"pid={ self ._omc_process .pid if isinstance (self ._omc_process , subprocess .Popen ) else '?' } " )
0 commit comments