Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | Related Pages

RunControl.py

00001 #!/usr/local/bin/python
00002 #
00003 #                               Copyright 2002
00004 #                                     by
00005 #                        The Board of Trustees of the
00006 #                     Leland Stanford Junior University.
00007 #                            All rights reserved.
00008 #
00009 
00010 __facility__ = "Online"
00011 __abstract__ = "GLAST LAT run control class"
00012 __author__   = "R. Claus <Claus@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online"
00013 __date__     = "10/11/2002"
00014 __version__  = "$Revision: 2.5 $"
00015 __credits__  = "SLAC"
00016 
00017 import LATTE.copyright_SLAC
00018 
00019 
00020 import                          os
00021 import                          sys
00022 import                          atexit
00023 import                          threading
00024 import logging           as     log
00025 from   qt                import *
00026 
00027 from   Queue             import Queue
00028 from   RunControlFSM     import RunControlFSM
00029 from   RunControlGUIImpl import RunControlGUIImpl
00030 
00031 from   LATTE.database.gVersions import Gversions
00032 from   LATTE.tools.guiBridges   import QtThreadBridge, PyThreadBridge
00033 from   LATTE.client.gLog        import logException
00034 from   LATTE.tools.taskEngine   import TaskEngine
00035 
00036 
00037 class ConnectState:
00038   Disconnected = 0
00039   Waiting      = 1
00040   Connected    = 2
00041 
00042 class RunControl(RunControlGUIImpl):
00043   """Class for providing a run control framework
00044   """
00045 
00046   GUI_CMD_INST    = QEvent.Type(QEvent.User + 1)
00047   GUI_CMD_FUNC    = QEvent.Type(QEvent.User + 2)
00048   GUI_CMD_FUNC_NR = QEvent.Type(QEvent.User + 3)
00049 
00050   def __init__(self, common, parent = None, name = None, fl = 0):
00051     RunControlGUIImpl.__init__(self, common, parent, name, fl)
00052 
00053     self.gui      = self                # For backward compatibility
00054 
00055     self.__common       = common
00056     self.__fsm          = None
00057     self.__actionEngine = None
00058     self.__hwVersions   = None
00059     self.__queue        = None
00060     self.__engine       = None
00061 
00062     # Set this object to be the client handler
00063     common.getCmdCli().handler(self)
00064     self.__connection_state = ConnectState.Disconnected
00065     #self.StopButton.setEnabled(False)
00066     #self.RunButton.setEnabled(False)
00067     #self.PauseButton.setEnabled(False)
00068 
00069     # Set up the GUI bridge
00070     self.initGUIbridges()
00071 
00072     self.__actionEngine = TaskEngine("FSMactionEngine")
00073     self.__actionEngine.start()
00074 
00075     self.commandSynch = TaskEngine("CommandSynch")
00076     self.commandSynch.start()
00077 
00078     self.eventHandler = TaskEngine("EventHandler")
00079     self.eventHandler.start()
00080 
00081     # Clean up at exit
00082     self.inShutdown = False
00083     atexit.register(self.__shutdown)
00084 
00085   def disconnected(self):
00086     self.__disconnect()
00087 
00088   def timedout(self):
00089     self.__disconnect()
00090 
00091   def __connection_notthere(self): # GUI thread
00092     #self.StopButton.setEnabled(False)
00093     #self.RunButton.setEnabled(False)
00094     #self.PauseButton.setEnabled(False)
00095     if self.__connection_state == ConnectState.Connected:
00096       log.error("OCS server disconnected, trying to reconnect")
00097       self.__connection_state = ConnectState.Waiting
00098       self.__common.disconnect() # RiC
00099       self.__connect()
00100 
00101   def __connection_established(self): # GUI thread
00102     log.info("OCS server is now connected")
00103     #self.StopButton.setEnabled(True)
00104     #self.RunButton.setEnabled(True)
00105     #self.PauseButton.setEnabled(True)
00106     self.__connection_state = ConnectState.Connected
00107 
00108   def __connection_failed(self, msg): # GUI thread
00109     if self.__tries == 0:
00110       log.error(msg)
00111     self.__tries += 1
00112     return self.__connection_state
00113 
00114   def connectClicked(self): # GUI thread
00115     if self.__connection_state is ConnectState.Disconnected:
00116       self.fileConnectAction.setMenuText("Disconnect")
00117       self.__connection_state = ConnectState.Waiting
00118       self.__tries = 0
00119       self.__connect()
00120     else:
00121       self.fileConnectAction.setMenuText("Connect")
00122       self.__connection_state = ConnectState.Disconnected
00123       self.__disconnect()
00124 
00125   def __connect(self):
00126     connecthdlr = threading.Thread(None, self.__connection_handler, 'ConnectHandler',())
00127     connecthdlr.setDaemon(True)
00128     connecthdlr.start()
00129 
00130   def __disconnect(self):
00131     self.execGUImethod(self.__connection_notthere)
00132 
00133   def __connection_handler(self):
00134     while (True):
00135       try:
00136         self.__common.connect()
00137         self.execGUImethodNR(self.__connection_established)
00138         break
00139       except RuntimeError, msg:
00140         state = self.execGUImethod(self.__connection_failed, msg)
00141         if state == ConnectState.Disconnected: break
00142         time.sleep(3)
00143 
00144   def userId(self):
00145     return 43231  #self.preferences()["userid"]
00146 
00147   def __statusMsg(self, msg, tmo):
00148     self.statusBar().message(msg, tmo)
00149 
00150   def __shutdown(self):
00151     """\brief Clean up and shut down the various threads.
00152     """
00153     # This method expects to be executed from the GUI thread.
00154 
00155     self.inShutdown = True
00156 
00157     # Bring the system into the RESET state
00158     self.doReset()
00159 
00160     # Use the following non-NR call to wait for the reset to finish
00161     self.execGUImethod(self.statusBar().message, "Exiting")
00162 
00163     # Shut down the task engines
00164     self.__actionEngine.shutdown()
00165     self.eventHandler.shutdown()
00166     self.commandSynch.shutdown()
00167 
00168     # After the FSM Action Engine has shut down we can delete the FSM
00169     if self.__fsm:
00170       del self.__fsm
00171       self.__fsm = None
00172 
00173   def __start(self, applName):
00174     ## Import the application
00175     #(appPath, appFile) = os.path.split(applName)
00176     #(appBase, appExt)  = os.path.splitext(appFile)
00177     #appStuff = self.__bml.find_module(appBase, (appPath,))
00178     #if appStuff is None:
00179     #  msg = 'Application %s is invalid' % applName
00180     #  logging.error(msg)
00181     #  return msg
00182     #module = self.__bml.load_module(appBase, appStuff)
00183     #if 'userApplication' not in dir(module):
00184     #  msg = 'Application %s is missing a userApplication class' % applName
00185     #  logging.error(msg)
00186     #  return msg
00187 
00188     import rcUtil
00189     try:
00190       dontReloadList = self.__common.options().noreload
00191       (module, filename) = rcUtil.importModule(appName=applName, reload=1, dontReloadList=dontReloadList)
00192     except Exception, e:
00193       logException()
00194       self.execGUImethodNR(self.__statusMsg, e[0], 0)
00195       return
00196 
00197     # Instantiate the application
00198     app = module.userApplication(self, self.__common.prefMan().userId(),
00199                                  self.__common.options().evtdebug is not None)
00200     log.info("Application %s loaded" % app.getName())
00201 
00202     # Set up the Finite State Machine
00203     self.__fsm = RunControlFSM(app)
00204 
00205     self.execGUImethodNR(self.__statusMsg, '%s: Loaded application %s' %
00206                          (self.__fsm.current_state, applName), 0)
00207 
00208   def SetupClicked(self):
00209     RunControlGUIImpl.SetupClicked(self)
00210     self.__actionEngine.spawn(self.__setup)
00211 
00212   def __setup(self):
00213     if self.__fsm is not None:
00214       self.__fsm.process('SETUP')
00215       self.__statusMsg(self.__fsm.current_state, 0)
00216 
00217   def TeardownClicked(self):
00218     RunControlGUIImpl.TeardownClicked(self)
00219     self.__actionEngine.spawn(self.__teardown)
00220 
00221   def __teardown(self):
00222     if self.__fsm is not None:
00223       self.__fsm.process('TEARDOWN')
00224       self.__statusMsg(self.__fsm.current_state, 0)
00225 
00226   def StartRunClicked(self):
00227     RunControlGUIImpl.StartRunClicked(self)
00228     self.__actionEngine.spawn(self.__run)
00229 
00230   def __run(self):
00231     if self.__fsm is not None:
00232       self.__fsm.process('START_RUN')
00233       self.__statusMsg(self.__fsm.current_state, 0)
00234 
00235   def StopRunClicked(self):
00236     RunControlGUIImpl.StopRunClicked(self)
00237     self.__actionEngine.spawn(self.__stoprun)
00238 
00239   def __stoprun(self):
00240     if self.__fsm is not None:
00241       self.__fsm.process('STOP_RUN')
00242       self.__statusMsg(self.__fsm.current_state, 0)
00243 
00244   def PauseClicked(self):
00245     RunControlGUIImpl.PauseClicked(self)
00246     self.__actionEngine.spawn(self.__pause)
00247 
00248   def __pause(self):
00249     if self.__fsm is not None:
00250       self.__fsm.process('PAUSE')
00251       self.__statusMsg(self.__fsm.current_state, 0)
00252 
00253   def ResumeClicked(self):
00254     RunControlGUIImpl.ResumeClicked(self)
00255     self.__actionEngine.spawn(self.__resume)
00256 
00257   def __resume(self):
00258     if self.__fsm is not None:
00259       self.__fsm.process('RESUME')
00260       self.__statusMsg(self.__fsm.current_state, 0)
00261 
00262   def StopClicked(self):
00263     RunControlGUIImpl.StopClicked(self)
00264     self.__actionEngine.spawn(self.__stop)
00265 
00266   def __stop(self):
00267     if self.__fsm is not None:
00268       self.__fsm.process('STOP')
00269       self.__statusMsg(self.__fsm.current_state, 0)
00270 
00271 
00272   def load(self):
00273     if self.__fsm is not None and self.__fsm.current_state != 'RESET':
00274       print "Applications are loadable only in the RESET state"
00275       self.__statusMsg('%s: Applications are loadable only in the RESET state' %
00276                        (self.__fsm.current_state), 0)
00277       return
00278 
00279     applName = RunControlGUIImpl.load(self)
00280     if applName is None:
00281       if self.__fsm is not None:
00282         self.__statusMsg('%s: Loading aborted' % (self.__fsm.current_state), 0)
00283       else:
00284         self.__statusMsg('Loading aborted', 0)
00285       return
00286 
00287     if self.__fsm is not None:
00288       self.__shutdown()
00289 
00290     # Start the application in its own thread after the reset from above
00291     # (if any) completes.
00292     self.__actionEngine.spawn(self.__start, str(applName))
00293 
00294     #msg = self.__start(str(applName))
00295     #if msg is None:
00296     #  self.__statusMsg('%s: Loaded application %s' %
00297     #                   (self.__fsm.current_state, applName), 0)
00298     #else:
00299     #  self.__statusMsg(msg, 0)
00300 
00301   def fileExit(self):
00302     self.close()
00303 
00304   def ExitButtonClicked(self):
00305     self.close()
00306 
00307   def doReset(self):
00308     """Emit a Reset button click under program control.
00309     Can be done from a subthread."""
00310     self.doEmit(self.ResetButton)
00311 
00312   def doStop(self):
00313     """Emit a Stop button click under program control.
00314     Can be done from a subthread."""
00315 
00316     # Ignore the doStop for this RunControl GUI
00317     return
00318     self.doEmit(self.StopButton)
00319 
00320   def doStopRun(self):
00321     """Depricated version of doStop()."""
00322     self.doStop()
00323 
00324   def doRun(self):
00325     """Emit a Run button click under program control.
00326     Can be done from a subthread."""
00327     self.doEmit(self.RunButton)
00328 
00329   def doPause(self):
00330     """Emit a Pause button click under program control.
00331     Can be done from a subthread."""
00332     self.doEmit(self.PauseButton)
00333 
00334   def doEmit(self, obj, signal = SIGNAL("clicked()"), args = ()):
00335     """Emit a signal, possibly from a subthread.
00336     By default the signal is 'clicked()' with no arguments."""
00337     if self.isGUIthread():
00338       obj.emit(signal, args)
00339     else:
00340       self.execGUImethodNR(obj.emit, signal, args)
00341 
00342   def normalCursor(self):
00343     self.execGUImethodNR(self.setCursor, QCursor(QWidget.ArrowCursor))
00344 
00345   def waitCursor(self):
00346     self.execGUImethodNR(self.setCursor, QCursor(QWidget.WaitCursor))
00347 
00348   def getHardwareVersions(self, lat=None):
00349     if self.__hwVersions is None:
00350       self.__hwVersions = Gversions.getHardwareVersions(lat)
00351     return self.__hwVersions
00352 
00353   def clearHardwareVersionsCache(self):
00354     self.__hwVersions = None
00355 
00356   def getFSM(self):
00357     return self.__fsm
00358 
00359   def setLAT(self, lat):
00360     self.__lat = lat
00361 
00362   def initGUIbridges(self):
00363     self.__thread = threading.currentThread()
00364     inQueue  = Queue()
00365     outQueue = Queue()
00366     self.__qtBridge = QtThreadBridge("QtBridge", outQueue, inQueue)
00367     self.__qtBridge.start()
00368     self.__pyBridge = PyThreadBridge("PyBridge", inQueue, outQueue)
00369 
00370   def isGUIthread(self):
00371     return threading.currentThread() == self.__thread
00372 
00373   def customEvent(self, e):
00374     # This method overrides the QObject base class's
00375     data = e.data()
00376     qtBridge = data[-1]
00377     if e.type() == RunControl.GUI_CMD_INST:
00378       qtBridge.pushResponse(data[0](*data[1:-2], **data[-2]))
00379     elif e.type() == RunControl.GUI_CMD_FUNC:
00380       qtBridge.pushResponse(data[0](*data[1:-2], **data[-2]))
00381     elif e.type() == RunControl.GUI_CMD_FUNC_NR:
00382       data[0](*data[1:-2], **data[-2])
00383 
00384   def createGUI(self, obj, *args, **kwargs):
00385     """Method used for instantiating a GUI from a non-GUI subthread"""
00386     self.__pyBridge.pushEvent((RunControl.GUI_CMD_INST, self, (obj,)+args+(kwargs,)))
00387     return self.__pyBridge.pullResponse()
00388 
00389   def execGUImethodNR(self, func, *args, **kwargs):
00390     """Method used for executing a GUI function from a non-GUI thread.
00391     Use this method when no (useful) response is expected or
00392     when waiting for one could cause a deadlock.  Any response from the
00393     function is lost."""
00394     if self.inShutdown:  return # Don't try to update the GUI during shutdown
00395     self.__pyBridge.pushEvent((RunControl.GUI_CMD_FUNC_NR, self, (func,)+args+(kwargs,)))
00396 
00397   def execGUImethod(self, func, *args, **kwargs):
00398     """Method used for executing a GUI function from a non-GUI thread.
00399     Use this method when a response is expected from the function and
00400     when it is appropriate to wait for it (no deadlock arises)"""
00401     self.__pyBridge.pushEvent((RunControl.GUI_CMD_FUNC, self, (func,)+args+(kwargs,)))
00402     return self.__pyBridge.pullResponse()

Generated on Fri Jul 21 13:26:32 2006 for LATTE R04-12-00 by doxygen 1.4.3