1 # Copyright (C) 2007-2015 CEA/DEN, EDF R&D
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.
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.
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
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 # Author : Adrien Bruneton (CEA)
22 import PVSERVER_ORB__POA
23 import SALOME_ComponentPy
24 import SALOME_DriverPy
28 import subprocess as subp
30 from time import sleep
32 #from SALOME_utilities import MESSAGE
35 """ Debug function """
37 #os.system("echo \"%s\" >> /tmp/paravis_log.txt" % m)
40 """ The core implementation (non CORBA, or Study related).
41 See the IDL for the documentation.
43 MAX_PVSERVER_PORT_TRIES = 1000 # Maximum number of tries to get a free port for the PVServer
44 PVSERVER_DEFAULT_PORT = 11111 # First port being tried to launch the pvserver
47 self.pvserverPort = -1
48 self.pvserverPop = None # Popen object from subprocess module
50 self.isGUIConnected = False # whether there is an active connection from the GUI.
55 raise Exception("PVSERVER_Impl.__init__ : \"import paraview\" failed !")
56 # deduce dynamically PARAVIEW_ROOT_DIR from the paraview module location
57 self.PARAVIEW_ROOT_DIR = None
58 ZE_KEY_TO_FIND_PV_ROOT_DIR="lib"
59 li=tmp.split(os.path.sep) ; li.reverse()
60 if ZE_KEY_TO_FIND_PV_ROOT_DIR not in li:
61 raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.INTERNAL_ERROR,
62 "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),
64 li=li[li.index("lib")+1:] ; li.reverse()
65 self.PARAVIEW_ROOT_DIR = os.path.sep.join(li)
68 Private. Identify a free port to launch the PVServer.
69 This is done by trying to bind a socket on the port.
70 We are still subject to a race condition between this detection mechanism and the actual launch of the pvserver
73 def __getFreePort(self, startPort):
76 while cnt < self.MAX_PVSERVER_PORT_TRIES:
78 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
79 s.bind(('', currPort))
82 except socket.error as e:
86 raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.INTERNAL_ERROR,
87 "[PVSERVER] maximum number of tries to retrieve a free port for the PVServer",
90 def FindOrStartPVServer( self, port ):
91 MESSAGE("[PVSERVER] FindOrStartPVServer ...")
94 if self.pvserverPop is None:
97 # Poll active server to check if still alive
98 self.pvserverPop.poll()
99 if not self.pvserverPop.returncode is None: # server terminated
103 return "cs://%s:%d" % (host, self.pvserverPort)
105 # (else) Server not alive, start it:
106 pvServerPath = os.path.join(self.PARAVIEW_ROOT_DIR, 'bin', 'pvserver')
109 port = self.__getFreePort(self.PVSERVER_DEFAULT_PORT)
110 self.pvserverPop = subp.Popen([pvServerPath, "--multi-clients", "--server-port=%d" % port, "--use-offscreen-rendering"])
111 sleep(3) # Give some time to the server to start up to avoid
112 # ugly messages on the client side saying that it cannot connect
113 # Is PID still alive? If yes, consider that the launch was successful
114 self.pvserverPop.poll()
115 if self.pvserverPop.returncode is None:
117 self.pvserverPort = port
118 MESSAGE("[PVSERVER] pvserver successfully launched on port %d" % port)
120 raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.INTERNAL_ERROR,
121 "[PVSERVER] Unable to start PVServer on port %d!" % port,
123 return "cs://%s:%d" % (host, self.pvserverPort)
125 def StopPVServer( self ):
126 MESSAGE("[PVSERVER] Trying to stop PVServer (sending KILL) ...")
127 if not self.pvserverPop is None:
128 self.pvserverPop.poll()
129 if self.pvserverPop.returncode is None:
130 # Terminate if still running:
131 self.pvserverPop.terminate()
132 MESSAGE("[PVSERVER] KILL signal sent.")
134 MESSAGE("[PVSERVER] Nothing to kill.")
137 def PutPythonTraceStringToEngine( self, t ):
140 def GetPythonTraceString(self):
141 return self.lastTrace
143 def SetGUIConnected( self, isConnected ):
144 self.isGUIConnected = isConnected
146 def GetGUIConnected( self ):
147 return self.isGUIConnected
149 class PVSERVER(PVSERVER_ORB__POA.PVSERVER_Gen,
150 SALOME_ComponentPy.SALOME_ComponentPy_i,
151 SALOME_DriverPy.SALOME_DriverPy_i,
154 Construct an instance of PVSERVER module engine.
155 The class PVSERVER implements CORBA interface PVSERVER_Gen (see PVSERVER_Gen.idl).
156 It is inherited from the classes SALOME_ComponentPy_i (implementation of
157 Engines::EngineComponent CORBA interface - SALOME component) and SALOME_DriverPy_i
158 (implementation of SALOMEDS::Driver CORBA interface - SALOME module's engine).
160 def __init__ ( self, orb, poa, contID, containerName, instanceName,
162 SALOME_ComponentPy.SALOME_ComponentPy_i.__init__(self, orb, poa,
163 contID, containerName, instanceName, interfaceName, 0)
164 SALOME_DriverPy.SALOME_DriverPy_i.__init__(self, interfaceName)
165 PVSERVER_Impl.__init__(self)
167 self._naming_service = SALOME_ComponentPy.SALOME_NamingServicePy_i( self._orb )
170 """ Override base class destroy to make sure we try to kill the pvserver
176 SALOME_ComponentPy.destroy(self)
179 Get version information.
181 def getVersion( self ):
182 import salome_version
183 return salome_version.getVersion("PARAVIS", True)
186 return PVSERVER_utils.getEngineIOR()
191 def createObject( self, study, name ):
192 MESSAGE("createObject()")
193 self._createdNew = True # used for getModifiedData method
194 builder = study.NewBuilder()
195 father = findOrCreateComponent( study )
196 object = builder.NewObject( father )
197 attr = builder.FindOrCreateAttribute( object, "AttributeName" )
198 attr.SetValue( name )
199 attr = builder.FindOrCreateAttribute( object, "AttributeLocalID" )
200 attr.SetValue( PVSERVER_utils.objectID() )
204 Dump module data to the Python script.
206 def DumpPython( self, study, isPublished, isMultiFile ):
207 MESSAGE("dumpPython()")
208 abuffer = self.GetPythonTraceString().split("\n")
210 abuffer = [ " " + s for s in abuffer ]
211 abuffer[0:0] = [ "def RebuildData( theStudy ):" ]
212 abuffer += [ " pass" ]
214 return ("\n".join( abuffer ), 1)
217 Import file to restore module data
219 def importData(self, studyId, dataContainer, options):
220 MESSAGE("importData()")
222 obj = self._naming_service.Resolve("myStudyManager")
223 myStudyManager = obj._narrow(SALOMEDS.StudyManager)
224 study = myStudyManager.GetStudyByID(studyId)
225 # create all objects from the imported stream
226 stream = dataContainer.get()
227 for objname in stream.split("\n"):
228 if len(objname) != 0:
229 self.createObject(study, objname)
230 self._createdNew = False # to store the modification of the study information later
231 return ["objects"] # identifier what is in this file
233 def getModifiedData(self, studyId):
234 MESSAGE("getModifiedData()")
237 obj = self._naming_service.Resolve("myStudyManager")
238 myStudyManager = obj._narrow(SALOMEDS.StudyManager)
239 study = myStudyManager.GetStudyByID(studyId)
240 # iterate all objects to get their names and store this information in stream
242 father = study.FindComponent( moduleName() )
244 iter = study.NewChildIterator( father )
246 name = iter.Value().GetName()
247 stream += name + "\n"
249 # store stream to the temporary file to send it in DataContainer
250 dataContainer = SALOME_DataContainerPy_i(stream, "", "objects", False, True)
251 aVar = dataContainer._this()