]> SALOME platform Git repositories - modules/paravis.git/blob - src/ENGINE/PVSERVER.py
Salome HOME
Turning PARAVIS into a Light module
[modules/paravis.git] / src / ENGINE / PVSERVER.py
1 # Copyright (C) 2007-2015  CEA/DEN, EDF R&D
2 #
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
7 #
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 # Lesser General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 #
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19 # Author : Adrien Bruneton (CEA)
20 #
21
22 import PVSERVER_ORB__POA
23 import SALOME_ComponentPy
24 import SALOMEDS
25 import SALOME
26 import PVSERVER_utils
27 import subprocess as subp
28 import socket
29 from time import sleep
30 import os
31 #from SALOME_utilities import MESSAGE
32
33 def MESSAGE(m):
34     """ Debug function """
35     pass
36     #os.system("echo \"%s\" >> /tmp/paravis_log.txt" % m)
37
38 class PVSERVER_Impl:
39     """ The core implementation (non CORBA, or Study related).
40         See the IDL for the documentation.
41     """
42     MAX_PVSERVER_PORT_TRIES = 1000  # Maximum number of tries to get a free port for the PVServer
43     PVSERVER_DEFAULT_PORT = 11111   # First port being tried to launch the pvserver
44     
45     def __init__(self):
46         self.pvserverPort = -1
47         self.pvserverPop = None  # Popen object from subprocess module
48         self.isGUIConnected = False  # whether there is an active connection from the GUI.
49         try:
50             import paraview
51             tmp=paraview.__file__
52         except:
53             raise Exception("PVSERVER_Impl.__init__ : \"import paraview\" failed !")
54         # deduce dynamically PARAVIEW_ROOT_DIR from the paraview module location
55         self.PARAVIEW_ROOT_DIR = None
56         ZE_KEY_TO_FIND_PV_ROOT_DIR="lib"
57         li=tmp.split(os.path.sep) ; li.reverse()
58         if ZE_KEY_TO_FIND_PV_ROOT_DIR not in li:
59             raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.INTERNAL_ERROR,
60                       "PVSERVER_Impl.__init__ : error during dynamic deduction of PARAVIEW_ROOT_DIR : Loc of paraview module is \"%s\" ! \"%s\" is supposed to be the key to deduce it !"%(tmp,ZE_KEY_TO_FIND_PV_ROOT_DIR),
61                       "PVSERVER.py", 0))
62         li=li[li.index("lib")+1:] ; li.reverse()
63         self.PARAVIEW_ROOT_DIR = os.path.sep.join(li)
64
65     """
66     Private. Identify a free port to launch the PVServer. 
67     This is done by trying to bind a socket on the port.
68     We are still subject to a race condition between this detection mechanism and the actual launch of the pvserver
69     itself ...
70     """
71     def __getFreePort(self, startPort):
72         cnt = 0
73         currPort = startPort
74         while cnt < self.MAX_PVSERVER_PORT_TRIES:
75             try:
76                 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
77                 s.bind(('', currPort))
78                 s.close()
79                 return currPort
80             except socket.error as e:
81                 cnt += 1
82                 currPort += 1
83                 pass
84         raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.INTERNAL_ERROR,
85                             "[PVSERVER] maximum number of tries to retrieve a free port for the PVServer",
86                             "PVSERVER.py", 0))
87                                            
88     def FindOrStartPVServer( self, port ):
89         MESSAGE("[PVSERVER] FindOrStartPVServer ...")
90         host = "localhost"
91         alive = True
92         if self.pvserverPop is None:
93             alive = False
94         else:
95             # Poll active server to check if still alive
96             self.pvserverPop.poll()
97             if not self.pvserverPop.returncode is None:  # server terminated
98                 alive = False
99         
100         if alive:
101             return "cs://%s:%d" % (host, self.pvserverPort)  
102           
103         # (else) Server not alive, start it:
104         pvServerPath = os.path.join(self.PARAVIEW_ROOT_DIR, 'bin', 'pvserver')
105         opt = []
106         if port <= 0:
107             port = self.__getFreePort(self.PVSERVER_DEFAULT_PORT)
108         self.pvserverPop = subp.Popen([pvServerPath, "--multi-clients", "--server-port=%d" % port, "--use-offscreen-rendering"])
109         sleep(3)  # Give some time to the server to start up to avoid 
110                   # ugly messages on the client side saying that it cannot connect
111         # Is PID still alive? If yes, consider that the launch was successful
112         self.pvserverPop.poll()
113         if self.pvserverPop.returncode is None:
114             success = True
115             self.pvserverPort = port
116             MESSAGE("[PVSERVER] pvserver successfully launched on port %d" % port)
117         else:
118             raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.INTERNAL_ERROR,
119                             "[PVSERVER] Unable to start PVServer on port %d!" % port,
120                             "PVSERVER.py", 0))
121         return "cs://%s:%d" % (host, self.pvserverPort)
122
123     def StopPVServer( self ):
124         MESSAGE("[PVSERVER] Trying to stop PVServer (sending KILL) ...")
125         if not self.pvserverPop is None:
126             self.pvserverPop.poll()
127             if self.pvserverPop.returncode is None:
128                 # Terminate if still running:
129                 self.pvserverPop.terminate()
130                 MESSAGE("[PVSERVER] KILL signal sent.")
131                 return True
132         MESSAGE("[PVSERVER] Nothing to kill.")
133         return False
134       
135     def SetGUIConnected( self, isConnected ):
136         self.isGUIConnected = isConnected
137         
138     def GetGUIConnected( self ):
139         return self.isGUIConnected
140     
141 class PVSERVER(PVSERVER_ORB__POA.PVSERVER_Gen,
142               SALOME_ComponentPy.SALOME_ComponentPy_i,
143               PVSERVER_Impl):
144     """
145     Construct an instance of PVSERVER module engine.
146     The class PVSERVER implements CORBA interface PVSERVER_Gen (see PVSERVER_Gen.idl).
147     It is inherited from the classes SALOME_ComponentPy_i (implementation of
148     Engines::EngineComponent CORBA interface - SALOME component).
149     """
150     def __init__ ( self, orb, poa, contID, containerName, instanceName, 
151                    interfaceName ):
152         SALOME_ComponentPy.SALOME_ComponentPy_i.__init__(self, orb, poa,
153                     contID, containerName, instanceName, interfaceName, 0)
154         PVSERVER_Impl.__init__(self)
155         #
156         self._naming_service = SALOME_ComponentPy.SALOME_NamingServicePy_i( self._orb )
157         #
158
159     """ Override base class destroy to make sure we try to kill the pvserver
160         before leaving.
161     """
162     def destroy(self):
163         self.StopPVServer()
164         # Invokes super():
165         SALOME_ComponentPy.destroy(self)
166       
167     """
168     Get version information.
169     """
170     def getVersion( self ):
171         import salome_version
172         return salome_version.getVersion("PARAVIS", True)
173
174     """
175     Get engine IOR
176     """
177     def GetIOR(self):
178         return PVSERVER_utils.getEngineIOR()