Salome HOME
ADD a end user module (services.py) to help the manipulation of SALOME KERNEL service...
authorboulant <boulant>
Wed, 26 Oct 2011 16:15:01 +0000 (16:15 +0000)
committerboulant <boulant>
Wed, 26 Oct 2011 16:15:01 +0000 (16:15 +0000)
src/KERNEL_PY/kernel/Makefile.am
src/KERNEL_PY/kernel/kernelpy_autotest.sh.in
src/KERNEL_PY/kernel/services.py
src/KERNEL_PY/kernel/threadhelper.py [new file with mode: 0644]

index 73a02e2b4de9bd5e62c6f36b483b13ccb2f00871..91c7852c78b05790d1932a5cd589d22fb25a41b3 100644 (file)
@@ -21,21 +21,27 @@ include $(top_srcdir)/salome_adm/unix/make_common_starter.am
 
 mypkgpythondir =$(salomepypkgdir)/kernel
 
+# This first set of modules could be used in any context, you just
+# have to get a python environement.
 mypkgpython_PYTHON = \
-       __init__.py \
-       deprecation.py \
-       logger.py \
-       studyedit.py \
-       termcolor.py \
-       logconfig.py \
-       unittester.py  \
+       __init__.py      \
+       deprecation.py   \
+       logger.py        \
+       termcolor.py     \
+       logconfig.py     \
+       unittester.py    \
        pyunittester.py  \
-       enumerate.py   \
-       uiexception.py \
-       datamodeler.py \
-       diclookup.py \
-       services.py \
-       testdata.py
+       enumerate.py     \
+       uiexception.py   \
+       datamodeler.py   \
+       testdata.py      \
+       diclookup.py     \
+       threadhelper.py
+
+# This set of modules required a running SALOME application.
+mypkgpython_PYTHON += \
+       studyedit.py     \
+       services.py
 
 bin_SCRIPTS=\
        kernelpy_autotest.sh
index f2dd3337e48ec52c1e91b37e7dd9fa7daeaf3b0d..2608dbb95a46e9c5a058644cce66a490fcacea41 100644 (file)
@@ -52,9 +52,9 @@ listfiles="\
 #    logconfig.py"
 
 # This list REQUIRES a running SALOME application
-#listfiles=$listfiles"\
-#    services.py \
-#    studyedit.py"
+listfiles=$listfiles"\
+    services.py \
+    studyedit.py"
 
 # This files are not used yet (but we test them)
 #listfiles=$listfiles"\
index 304cd2f0b1ac5c88c68f15fdfac099242fda4ca1..d80046507eafcc49521e0fd79cb66d46ddd75a7e 100644 (file)
 
 # Author: Guillaume Boulant (EDF/R&D)
 
+#
+# WARNING: development notes
+#
+# This python module is aimed to be a (yet another?) end user
+# interface to manipulate the KERNEL SALOME services. Note that it
+# does not replace the salome module (i.e. what you get when typing
+# "import salome", actually implemented in the file __init__.py of the
+# salome python package). It provides instead functions that help to
+# use the salome module in a end user context (development of domain
+# specific tools by programmers that are not ten years experienced in
+# SALOME development).
 
 import salome
 if salome.lcc is None:
@@ -27,7 +38,25 @@ if salome.lcc is None:
         salome.salome_init()
     except RuntimeError, e:
         print e
-    
+
+# Note that the salome module provides you with standard SALOME
+# objects: CORBA broker (orb): salome.orb lyfe cycle (lcc) :
+# salome.lcc naming service : salome.naming_service study manager :
+# salome.myStudyManager The default study : salome.myStudy
+#
+# Alternatively, you may obtain these objects directly with the
+# following instructions:
+#
+#   from omniORB import CORBA
+#   from LifeCycleCORBA import LifeCycleCORBA
+#   orb = CORBA.ORB_init( [''], CORBA.ORB_ID )
+#   lcc = LifeCycleCORBA( orb )
+# 
+#   from SALOME_NamingServicePy import SALOME_NamingServicePy_i
+#   naming_service = SALOME_NamingServicePy_i( orb )
+#
+# (See salome.py page in the KERNEL documentation)
+
 #
 # ==============================================================================
 # Helper functions for SALOME components
@@ -57,6 +86,9 @@ if salome.lcc is None:
 def getComponent(componentName = "SalomeTestComponent",
                  corbaModule   = "Engines",
                  containerType = "FactoryServer"):
+    """
+    Get a SALOME CORBA component from its name
+    """
     print "INF: getting component %s from CORBA module %s ..."%(componentName,corbaModule)
     __import__(corbaModule)
     component=salome.lcc.FindOrLoadComponent(containerType,componentName)
@@ -70,12 +102,30 @@ def getComponent(componentName = "SalomeTestComponent",
 # the list of components defined in the current session.
 import SALOME_ModuleCatalog
 def getComponentList():
+    """
+    Get the list of names of all SALOME componenents register in
+    the catalog.
+    """
     obj = salome.naming_service.Resolve('Kernel/ModulCatalog')
     catalog = obj._narrow(SALOME_ModuleCatalog.ModuleCatalog)
     if not catalog:
         raise RuntimeError, "Can't accesss module catalog"
     return catalog.GetComponentList()
 
+import SALOMEDS
+def getStudyManager():
+    """
+    Get a study manager to create and manage SALOME studies. WARN: you
+    should use instead the variable salome.myStudyManager. This
+    function is given for illustration of usage of the naming service
+    """
+    import SALOMEDS
+    naming_service = SALOME_NamingServicePy_i( orb )
+    obj = naming_service.Resolve( '/myStudyManager' )
+    studyManager = obj._narrow( SALOMEDS.StudyManager)
+    return studyManager
+
+
 #
 # ==============================================================================
 # Basic use cases and unit tests
@@ -99,5 +149,5 @@ def TEST_getComponentList():
 
 if __name__ == "__main__":
     import unittester
-    unittester.run("salome/kernel/__init__","TEST_getComponent")
-    unittester.run("salome/kernel/__init__","TEST_getComponentList")
+    unittester.run("services","TEST_getComponent")
+    unittester.run("services","TEST_getComponentList")
diff --git a/src/KERNEL_PY/kernel/threadhelper.py b/src/KERNEL_PY/kernel/threadhelper.py
new file mode 100644 (file)
index 0000000..ba9c6f5
--- /dev/null
@@ -0,0 +1,385 @@
+# -*- coding: iso-8859-1 -*-
+#  Copyright (C) 2007-2008  CEA/DEN, EDF R&D, OPEN CASCADE
+#
+#  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+#  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2.1 of the License.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+#  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+__author__="gboulant"
+__date__ ="$1 avr. 2010 18:12:38$"
+
+import time
+import threading
+
+# ===========================================================================
+class Runner(threading.Thread):
+    """
+    This class provides a tool to run and drive a function in a dedicated thread.
+    """
+    def __init__(self, functionToRun=None,*argv):
+        threading.Thread.__init__( self )
+        self._stopevent = threading.Event()
+        self.setFunction(functionToRun)
+        self.setArguments(*argv)
+        self._exception = None
+        self._return    = None
+        self._observer  = None
+        self._callbackFunction = None
+        # We create an id from the name and the time date in milliseconds
+        self._id = functionToRun.__name__ +"/"+ str(time.time())
+
+    def setFunction(self,functionToRun):
+        """
+        Positionne la fonction � ex�cuter. La fonction peut �tre la
+        m�thode d'un objet pass�e sous la forme 'monobjet.mamethode'.
+        """
+        self.clear()
+        self._function = functionToRun
+
+    def setArguments(self,*argv):
+        """
+        Positionne les arguments � passer � la fonction
+        """
+        self.clear()
+        self._argv = argv
+
+    def getReturn(self):
+        """
+        Retourne le resultat de la fonction. En cas d'erreur, on
+        r�cup�rera l'exception lev�e au moyen de la m�thode
+        getException().
+        """
+        return self._return
+
+    def setCallbackFunction(self, callbackFunction):
+        self._callbackFunction = callbackFunction
+
+    def setObserver(self, observerToNotify):
+        """
+        Permet de sp�cifier un observateur � notifier en fin
+        d'ex�cution. L'observateur est suppos� �tre un objet qui
+        impl�mente une m�thode processNotification()
+        """
+        try:
+            observerToNotify.processNotification
+        except AttributeError:
+            raise DevelException("The observer should implement the method processNotification()")
+
+        self._observer = observerToNotify
+
+    def run(self):
+        """
+        Ex�cution de la fonction. Impl�mentation de la m�thode run
+        d�clench�e par l'appel � Thread.start().
+        """
+        print "##################### threadhelper.run"
+        if self._function is None: return
+        try:
+            self._return = self._function(*self._argv)
+        except Exception, e:
+            print e
+            self._exception = e
+        self._stopevent.set()
+        self.notifyObserver()
+        self.callback()
+        # _GBO_ Maybe it's no use to have both observers and a callback function.
+        # One of the two mechanism should be sufficient
+
+    def notifyObserver(self):
+        if self._observer is None:
+            # Aucune notification pr�vue
+            return
+        try:
+            self._observer.processNotification()
+        except AttributeError, att:
+            if str(att) == "processNotification":
+                print "L'observateur n'impl�mente pas la m�thode processNotification()"
+            else:
+                print "La fonction processNotification() a lev� une exception:"
+                print att
+        except Exception, e:
+            print "La fonction processNotification() a lev� une exception:"
+            print e
+
+    def callback(self):
+        if self._callbackFunction is None: return
+        self._callbackFunction()
+
+    def isEnded(self):
+        """
+        Retourne true si la fonction s'est termin�e naturellement
+        (correctement ou en erreur). Utile pour les client qui
+        pratique le pooling (interrogation r�p�t�e � intervalle
+        r�gulier.
+        """
+        return self._stopevent.isSet()
+        # _GBO_ On n'utilise pas isAlive() pour pouvoir ma�triser
+        # l'indicateur de stop (exemple de la fonction kill)
+        # return not self.isAlive()
+
+    def getException(self):
+        return self._exception
+
+    def getId(self):
+        return self._id
+
+    def wait(self,timeout=None):
+        """
+        Met fin au thread apr�s timeout seconde s'il est encore en
+        ex�cution.
+        Si le compte-�-rebours est atteind sans que la fonction
+        functionToRun soit termin�e, alors le runner est laiss� dans
+        l'�tat 'not Ended', c'est-�-dire que isEnded retourne
+        false. On peut ainsi distinguer l'arr�t normal de l'arr�t �
+        l'issue du timeout.
+        """
+        threading.Thread.join(self, timeout)
+
+
+    def kill(self,message="Arr�t demand�"):
+        """
+        Cette m�thode doit �tre appeler pour interrombre le
+        thread. Cet appel d�clare le thread comme en erreur (exception
+        mise � disposition par la m�thode getException().
+        """
+        # On cr�e un exception indiquant la demande d'interruption
+        self._exception = Exception(message)
+        self._stopevent.set()
+        # _GBO_ ATTENTION
+        # Un thread python ne peut pas en r�alit� �tre int�rrompu.
+        # La fonction reste donc en ex�cution m�me si on a rejoint le
+        # thread appelant.
+
+    def clear(self):
+        self._stopevent.clear()
+        self._exception = None
+        self._return    = None
+
+# ===========================================================================
+CONTINUE=1
+STOP=0
+
+class PeriodicTimer( threading.Thread ):
+    """
+    Cette classe permet d'amorcer un compte-�-rebours p�riodique pour
+    op�rer un m�canisme de pooling. On d�finit la fonction appell�e
+    p�riodiquement et la fonction terminale.
+    """
+    def __init__( self,  loopdelay, initdelay=0,
+                  periodic_action=None,
+                  ended_action=None,
+                  *ended_argv ):
+
+        if periodic_action is None:
+            self._periodic_action = self.defaultPeriodicAction
+        else:
+            self._periodic_action = periodic_action
+        self._ended_action = ended_action
+        self._loopdelay = loopdelay
+        self._initdelay = initdelay
+        self._running = CONTINUE
+        self._ended_argv    = ended_argv
+        threading.Thread.__init__( self )
+
+    def defaultPeriodicAction():
+        """
+        Les fonctions 'periodicAction' retournent CONTINU ou STOP
+        apr�s avoir ex�cut� l'action de fin de boucle. Si STOP est
+        retourn�, le cycle est interrompu. 
+        """
+        return CONTINU
+
+    def run( self ):
+        if self._initdelay:
+            time.sleep( self._initdelay )
+        self._runtime = time.time()
+        while self._running == CONTINUE:
+            start = time.time()
+            self._running  = self._periodic_action()
+            self._runtime += self._loopdelay
+            time.sleep( max( 0, self._runtime - start ) )
+        if self._ended_action is not None:
+            self._ended_action( *self._ended_argv )
+
+    def stop( self ):
+        self._running = STOP
+
+
+
+#
+# ======================================================
+# Fonctions de test unitaire
+# ======================================================
+#
+
+# ======================================================
+import os
+testfilename="/tmp/threadhelperTestFile"
+def testIfContinue():
+    print "On examine la pr�sence du fichier ", testfilename
+    if os.path.exists(testfilename):
+        return STOP
+    else:
+        return CONTINUE
+
+def endedAction():
+    print "FINI"
+
+def TEST_PeriodicTimer():
+    periodicTimer=PeriodicTimer(1,0,testIfContinue, endedAction)
+    periodicTimer.start()
+
+
+# ======================================================
+def function_ok(nbsteps=5):
+    """
+    Fonction qui se termine correctement
+    """
+    print "D�but"
+    cnt=0
+    while ( cnt < nbsteps ):
+        print "Etape ", cnt
+        time.sleep(0.6)
+        cnt+=1
+
+    print "Fin"
+
+def function_with_exception():
+    """
+    Fonction qui aboutie � une lev�e d'exception
+    """
+    print "D�but"
+    cnt=0
+    while ( cnt < 5 ):
+        print "Etape ", cnt
+        time.sleep(1)
+        cnt+=1
+    
+    raise Exception("erreur d'ex�cution de la fonction")
+    print "Fin"
+
+def infinite_function():
+    """
+    fonction de dur�e infinie (tant qu'il y a du courant �l�ctrique) pour
+    le test du timeout.
+    """
+    print "D�but"
+    cnt=0
+    while ( 1 ):
+        print "Etape ", cnt
+        time.sleep(1)
+        cnt+=1
+    
+    raise Exception("erreur")
+    print "Fin"
+
+
+def runWithRunner(functionToRun):
+    """
+    Ex�cute la fonction avec le runner. On illustre ici la modalit�
+    d'utilisation du Runner.
+    """
+    print "###########"
+    runner = Runner(functionToRun)
+    runner.start()
+
+    while ( not runner.isEnded() ):
+        print "La fonction est en cours"
+        time.sleep(0.2)
+    e = runner.getException()
+    if e is not None:
+        print "La fonction s'est termin�e en erreur"
+        print e
+        # On peut en fait la relancer
+        # raise e
+    else:
+        print "La fonction s'est termin�e correctement"
+
+
+def runWithTimeout(functionToRun, timeout=10):
+    """
+    Ex�cute la fonction avec le runner. On illustre ici la modalit�
+    d'utilisation du Runner.
+    """
+    print "runWithTimeout : DEBUT"
+    runner = Runner(functionToRun)
+    runner.start()
+
+    # On se fixe un temps au del� duquel on consid�re que la fonction
+    # est en erreur => on tue le thread (timeout)
+    runner.wait(timeout)
+    print "Apr�s runner.timeout(timeout)"
+    if not runner.isEnded():    
+        runner.kill()
+    e = runner.getException()
+    if e is not None:
+        print "La fonction s'est termin�e en erreur"
+        print e
+        # On peut en fait la relancer
+        # raise e
+    else:
+        print "La fonction s'est termin�e correctement"
+
+    print "runWithTimeout : FIN"
+    import sys
+    sys.exit(0)
+    
+
+def TEST_Timeout():
+    #runWithTimeout(function_ok)
+    #runWithTimeout(function_ok,2)
+    runWithTimeout(function_with_exception)
+
+    
+def TEST_Runner():
+    runWithRunner(function_ok)
+    runWithRunner(function_with_exception)
+    #runWithRunner(infinite_function)
+
+
+def myCallbackFunction():
+    print "myCallbackFunction: the job is ended"
+    
+
+def TEST_runWithCallback():
+    runner = Runner(function_ok,8)
+    runner.setCallbackFunction(myCallbackFunction)
+    runner.start()
+
+    if runner.getException() is not None:
+        return False
+
+    runnerId = runner.getId()
+    print "A runner has been started with id="+str(runnerId)
+    cpt = 0
+    while ( not runner.isEnded() ):
+        print "Waiting notification from process "+str(runner.getId())+", step n°"+str(cpt)
+        time.sleep(0.2)
+        cpt+=1
+
+    return True
+
+if __name__ == "__main__":
+    import unittester
+    #TEST_PeriodicTimer()
+    #TEST_Runner()
+    #TEST_Timeout()
+    unittester.run("threadhelper","TEST_runWithCallback")
+
+