00001 #!/usr/bin/env 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 main run control class" 00012 __author__ = "R. Claus <Claus@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online" 00013 __date__ = ("$Date: 2006/05/04 19:48:31 $").split(' ')[1] 00014 __version__ = "$Revision: 2.57 $" 00015 __release__ = "$Name: R04-12-00 $" 00016 __credits__ = "SLAC" 00017 00018 import LATTE.copyright_SLAC 00019 00020 import os 00021 import sys 00022 import atexit 00023 import threading 00024 import logging as log 00025 import types 00026 import time 00027 import gc 00028 import string 00029 00030 from qt import * 00031 import sip 00032 00033 from Queue import Queue 00034 from RunControlFSM import RunControlFSM 00035 from RunControlMainGUIImpl import RunControlMainGUIImpl 00036 from rcEndRunDlgImpl import rcEndRunDlgImpl 00037 00038 # Modules here are imported to prevent them from being reloaded 00039 # See rcUtil for more info 00040 00041 import Pyana 00042 import LDF 00043 00044 try: 00045 import cfitsio 00046 except ImportError, e: 00047 pass 00048 00049 try: 00050 import Numeric 00051 except ImportError, e: 00052 pass 00053 00054 import rcUtil 00055 import rcReportGen 00056 import rcRunIdHelper 00057 import rcArchiver 00058 00059 from LATTE.tools.taskEngine import TaskEngine 00060 from LATTE.tools.guiBridges import GUIbridge, QtThreadBridge, PyThreadBridge 00061 from LATTE.client.gLog import logException 00062 from LATTE.database.gVersions import Gversions, ROOT_DIR_ENVS 00063 from LATTE.database.gPlumber import Plumber 00064 00065 class ConnectState: 00066 Disconnected = 0 00067 Waiting = 1 00068 Connected = 2 00069 00070 class RunControlMain(RunControlMainGUIImpl): 00071 """Class for providing a run control framework 00072 """ 00073 00074 def __init__(self, common, parent = None, name = "RunControl", fl = 0): 00075 RunControlMainGUIImpl.__init__(self, common, parent, name, fl) 00076 00077 self.__common = common 00078 self.__fsm = None 00079 self.__actionEngine = None 00080 self.__app = None 00081 self.__browser = None 00082 self.__hwVersions = None 00083 self.__queue = None 00084 self.__engine = None 00085 self.__guiThread = threading.currentThread() 00086 self.__guiBridge = GUIbridge(self.__guiThread) 00087 00088 # Set this object to be the client handler 00089 common.getCmdCli().handler(self) 00090 self.__connection_state = ConnectState.Disconnected 00091 self.ResetButton.setEnabled(False) 00092 self.StopButton.setEnabled(False) 00093 self.RunButton.setEnabled(False) 00094 self.PauseButton.setEnabled(False) 00095 00096 self.__actionEngine = TaskEngine("FSMactionEngine") 00097 self.__actionEngine.start() 00098 00099 self.commandSynch = TaskEngine("CommandSynch") 00100 self.commandSynch.start() 00101 00102 self.eventHandler = TaskEngine("EventHandler") 00103 self.eventHandler.start() 00104 00105 # Pick an operator 00106 operator = common.prefMan().preferences()["operatorobj"] 00107 self.cmbOperator.setCurrentText(operator.getName()) 00108 00109 self.__endRunDlg = rcEndRunDlgImpl(parent=self) 00110 00111 # Clean up at exit 00112 self.inShutdown = False 00113 atexit.register(self.__shutdown) 00114 00115 # Preload modules specified in preferences 00116 preload = common.prefMan().preferences()["preload"] 00117 if preload.strip() != "": 00118 preloadList = map(string.strip, preload.split(',')) 00119 for m in preloadList: 00120 exec 'import ' + m 00121 00122 def disconnected(self): 00123 self.__disconnected() 00124 00125 def timedout(self): 00126 self.__disconnected() 00127 00128 def __connection_notthere(self): # GUI thread 00129 if self.__connection_state == ConnectState.Connected: 00130 self.StopButton.setEnabled(False) 00131 self.RunButton.setEnabled(False) 00132 self.PauseButton.setEnabled(False) 00133 log.error("OCS server disconnected, trying to reconnect") 00134 # As per LTE-311, return the run to RESET state 00135 self.__resetStoppedApp() 00136 self.__connection_state = ConnectState.Waiting 00137 self.__common.disconnect() 00138 self.__connect() 00139 00140 def __connection_established(self): # GUI thread 00141 log.info("OCS server is now connected") 00142 if self.__app is not None: 00143 self.StopButton.setEnabled(True) 00144 self.RunButton.setEnabled(True) 00145 self.PauseButton.setEnabled(True) 00146 self.__connection_state = ConnectState.Connected 00147 00148 def __resetStoppedApp(self): 00149 if (self.__app.__class__.__name__ == 'userSuite' \ 00150 and not self.__app.isRunning() 00151 ) or ( 00152 self.__app.__class__.__name__ == 'userApplication' and \ 00153 self.__fsm.current_state == 'STOPPED' 00154 ): 00155 log.warn("Returning the run to RESET state.") 00156 self.doReset() 00157 00158 def __connection_failed(self, msg): # GUI thread 00159 if self.__tries == 0: 00160 log.error(msg) 00161 self.__tries += 1 00162 return self.__connection_state 00163 00164 def connectClicked(self): # GUI thread 00165 if self.__connection_state is ConnectState.Disconnected: 00166 self.fileConnectAction.setMenuText("Disconnect") 00167 self.__connection_state = ConnectState.Waiting 00168 self.__tries = 0 00169 self.__connect() 00170 else: 00171 self.fileConnectAction.setMenuText("Connect") 00172 self.__connection_state = ConnectState.Disconnected 00173 self.__common.disconnect() 00174 self.StopButton.setEnabled(False) 00175 self.RunButton.setEnabled(False) 00176 self.PauseButton.setEnabled(False) 00177 log.info("OCS server is now disconnected") 00178 # As per LTE-311, return the run to RESET state 00179 self.__resetStoppedApp() 00180 00181 def __connect(self): 00182 connecthdlr = threading.Thread(None, self.__connection_handler, 'ConnectHandler',()) 00183 connecthdlr.setDaemon(True) 00184 connecthdlr.start() 00185 00186 def __disconnected(self): 00187 self.execGUImethod(self.__connection_notthere) 00188 00189 def __connection_handler(self): 00190 while (True): 00191 try: 00192 force = (self.__common.options().oesforce != None) 00193 self.__common.connect(force) 00194 self.__plumb() 00195 self.execGUImethod(self.__connection_established) 00196 break 00197 except RuntimeError, msg: 00198 state = self.execGUImethod(self.__connection_failed, msg) 00199 if state == ConnectState.Disconnected: break 00200 time.sleep(3) 00201 00202 def __plumb(self): 00203 if self.__common.options().noplumbing is not None: 00204 return 00205 if self.__common.getLAT() is not None: 00206 if self.__common.options().fswmsglevel is not None: 00207 msglevel = int(self.__common.options().fswmsglevel) 00208 else: 00209 msglevel = 3 00210 verbose = (self.__common.options().ocsverbose != None) 00211 force = (self.__common.options().initforce != None) 00212 plumber = Plumber(self.__common.getLAT(), self.__common.getXBRD()) 00213 plumber.initialize(msglevel, verbose, force) 00214 00215 def isConnected(self): 00216 """\brief Return the connection state. 00217 00218 Used by the WatchItem class in rcStatusMonitor. 00219 00220 \return Connection state 00221 """ 00222 return self.__connection_state == ConnectState.Connected 00223 00224 def schemaChanged(self): 00225 if self.__connection_state is ConnectState.Connected: 00226 self.__plumb() 00227 00228 def applyPermissions(self, operator): 00229 adminOverride = operator.isAdministrator() 00230 if adminOverride: 00231 self.fileExitAction.setEnabled(1) 00232 else: 00233 self.fileExitAction.setEnabled(0) 00234 if 'allow_python_shell' in operator.getPermissions() or adminOverride: 00235 self.viewPythonShell.setEnabled(1) 00236 else: 00237 self.viewPythonShell.setEnabled(0) 00238 if 'set_particle_type' in operator.getPermissions() or adminOverride: 00239 self.comboParticleType.setEnabled(1) 00240 else: 00241 self.comboParticleType.setEnabled(0) 00242 if 'set_orientation' in operator.getPermissions() or adminOverride: 00243 self.comboOrientation.setEnabled(1) 00244 else: 00245 self.comboOrientation.setEnabled(0) 00246 00247 if 'edit_preferences' in operator.getPermissions() or adminOverride: 00248 self.editPreferencesAction.setEnabled(1) 00249 else: 00250 self.editPreferencesAction.setEnabled(0) 00251 00252 for (panelLabel, panel) in self.panels().items(): 00253 if 'enable_' + panelLabel.lower() + '_panel' in operator.getPermissions() or adminOverride: 00254 self.setEnabledTabPage(panelLabel, 1) 00255 else: 00256 self.setEnabledTabPage(panelLabel, 0) 00257 if "applyPermissions" in dir(panel): 00258 panel.applyPermissions(operator) 00259 00260 if self.__common.getMSGLogger() is not None: 00261 if adminOverride: 00262 self.__common.getMSGLogger().setSecure(0) 00263 else: 00264 self.__common.getMSGLogger().setSecure(1) 00265 self.__common.getMSGLogger().show() 00266 00267 self.__common.prefGUI().applyPermissions(operator) 00268 00269 00270 def setEnabledTabPage(self, panelLabel, enable): 00271 """Find the tabPage belonging to panel identified by panelLabel 00272 """ 00273 for i in range(self.RunControlTab.count()): 00274 tabPage = self.RunControlTab.page(i) 00275 if panelLabel == str(self.RunControlTab.tabLabel(tabPage)): 00276 tabPage.setEnabled(enable) 00277 break 00278 00279 def setOperator(self, operator): 00280 self.cmbOperator.setCurrentText(operator) 00281 self.__common.prefMan().setOperator(operator) 00282 00283 def setParticleType(self, particleType): 00284 self.__common.prefMan().setParticleType(particleType) 00285 00286 def setOrientation(self, orientation): 00287 self.__common.prefMan().setOrientation(orientation) 00288 00289 def getName(self): 00290 return "RunControlMain" 00291 00292 def __statusMsg(self, msg, tmo): 00293 self.execGUImethodNR(self.statusBar().message, msg, tmo) 00294 00295 def __shutdown(self): 00296 """\brief Clean up and shut down the various threads. 00297 """ 00298 # This method expects to be executed from the GUI thread. 00299 00300 self.inShutdown = True 00301 00302 # Bring the system into the RESET state 00303 self.doReset() 00304 00305 # Use the following non-NR call to wait for the reset to finish 00306 self.execGUImethod(self.statusBar().message, "Exiting") 00307 00308 # Shut down the task engines 00309 self.__actionEngine.shutdown() 00310 self.eventHandler.shutdown() # RiC: timeout not needed with above wait 00311 self.commandSynch.shutdown() 00312 00313 # After the FSM Action Engine has shut down we can delete the FSM 00314 if self.__fsm: 00315 del self.__fsm 00316 self.__fsm = None 00317 00318 # Disconnect from the command and event servers 00319 self.__common.disconnect() 00320 00321 # Stop the data distribution server 00322 dds = self.__common.getDDS() 00323 if dds is not None: 00324 dds.disconnect() 00325 00326 # Stop the GUI bridge 00327 #self.__qtBridge.terminate() 00328 self.__guiBridge.shutdown() 00329 00330 00331 def initApp(self): 00332 """\brief Method usually invoked by the GUI to load a new application 00333 """ 00334 applName = self.OpenAppl.currentText() 00335 if str(applName) == "": return # Operator cancelled 00336 self.loadApp(str(applName)) 00337 00338 def loadApp(self, applName): 00339 """\brief Method that can be invoked from code to load a new application 00340 00341 \param applName Name of an application to load 00342 """ 00343 if self.__fsm is not None: 00344 self.ResetClicked() 00345 if applName is None: 00346 self.__statusMsg('Loading aborted', 0) 00347 return 00348 00349 # Start the application in its own thread after the reset from above 00350 # (if any) completes. 00351 self.__actionEngine.spawn(self.__startApp, str(applName)) 00352 00353 self.ResetButton.setEnabled(True) 00354 self.StopButton.setEnabled(True) 00355 self.RunButton.setEnabled(True) 00356 self.PauseButton.setEnabled(True) 00357 00358 def loadModule(self, module): 00359 """\brief Method that can be invoked from code to load a new module 00360 00361 \param module Pointer to module to load 00362 """ 00363 if self.__fsm is not None: 00364 self.ResetClicked() 00365 self.__actionEngine.spawn(self.__startModule, module) 00366 00367 def __initAppCommon(self, appType, app, appName, filename): 00368 msg = "%s %s loaded from %s" % (appType, app.getName(), filename) 00369 log.info(msg) 00370 00371 # Update the application history list box. 00372 self.execGUImethod(self.__updateAppListBox, appName) 00373 if self.__common.options().securedir is None and self.__common.options().norcoverride is None: 00374 self.execGUImethod(self.__updateRunConditions, 'NOT-DEFINED') 00375 00376 return msg 00377 00378 def __updateRunConditions(self, undef): 00379 prefMan = self.__common.prefMan() 00380 self.cmbOperator.setCurrentText(undef) 00381 prefMan.setOperator(undef) 00382 self.comboParticleType.setCurrentText(undef) 00383 prefMan.setParticleType(undef) 00384 self.comboOrientation.setCurrentText(undef) 00385 prefMan.setOrientation(undef) 00386 prefMan.setSite(undef) 00387 prefMan.setPhase(undef) 00388 prefMan.setInstrumentType(undef) 00389 00390 def __updateAppListBox(self, appName): 00391 if self.OpenAppl.listBox().findItem(appName) is None: 00392 self.OpenAppl.insertItem(appName) 00393 00394 def __startApp(self, appName): 00395 try: 00396 self.execGUImethod(self.statusMonitor().stopWatch) 00397 if self.__common.options().noreload is not None: 00398 dontReloadList = self.__common.getDBN()["_dontReload"] 00399 else: 00400 dontReloadList = [] 00401 00402 prefs = self.__common.prefMan().preferences() 00403 if prefs['dontreload'].strip() != "": 00404 dontReloadPrefs = map(string.strip, prefs['dontreload'].split(',')) 00405 else: 00406 dontReloadPrefs = [] 00407 dontReloadList += dontReloadPrefs 00408 00409 (module, filename) = rcUtil.importModule(appName=appName, 00410 reload=not prefs["disablereload"], 00411 dontReloadList=dontReloadList, 00412 showUnloadedModules=prefs["showunloaded"] ) 00413 except Exception, e: 00414 logException() 00415 if type(e) == types.ListType: 00416 self.__statusMsg(e[0], 0) 00417 else: 00418 self.__statusMsg(str(e), 0) 00419 return 00420 self.__clearPreviousApp() 00421 self.__setupApp(module, appName, filename) 00422 00423 def __startModule(self, module): 00424 self.__clearPreviousApp() 00425 appName = module.userModuleName 00426 filename = module.__file__ 00427 self.__setupApp(module, filename, filename) 00428 00429 def __clearPreviousApp(self): 00430 del self.__app 00431 self.__app = None 00432 gc.collect() 00433 if len(gc.garbage) != 0: 00434 log.error("RunControlMain: Found garbage: %s" % gc.garbage) 00435 00436 def __setupApp(self, module, appName, filename): 00437 userid = self.__common.prefMan().userId() 00438 evtdebug = self.__common.options().evtdebug is not None 00439 if 'userApplication' in dir(module): 00440 self.__app = module.userApplication(self, userid, evtdebug) 00441 msg = self.__initAppCommon('Application', self.__app, appName, filename) 00442 self.__fsm = RunControlFSM(self.__app) 00443 self.execGUImethodNR(self.__updateGUI, self.__fsm.current_state, False) 00444 self.execGUImethodNR(self.handleComments, None, self.__fsm.current_state) 00445 self.execGUImethod(self.statusMonitor().startWatch) 00446 elif 'userSuite' in dir(module): 00447 # Set the FSM to None so that we don't check for current state 00448 # in the case of a suite run. Fixes LTE-343 00449 self.__fsm = None 00450 self.__app = module.userSuite(self, userid, evtdebug) 00451 msg = self.__initAppCommon('Suite', self.__app, appName, filename) 00452 if self.__app.rcSetupSuite() is None: 00453 msg = "Suite setup failed...Aborting suite %s" % appName 00454 log.error(msg) 00455 else: 00456 self.execGUImethodNR(self.State.setText, "SUITE SETUP") 00457 else: 00458 msg = '%s is not a recognized user script. userApplication or userSuite class is missing.' % appName 00459 log.error(msg) 00460 self.__common.getDBN()['_app'] = self.__app 00461 self.__statusMsg(msg, 0) 00462 00463 def __updateGUI(self, state, enabled): 00464 """\brief Method for updating the GUI 00465 """ 00466 # This method may complete asynchronously to the application's activities 00467 # since the application doesn't depend on the results. Should this ever 00468 # change, be sure to change the execGUImethodNR() calls below to 00469 # execGUImethod() calls so that the fsmActionEngine waits for __updateGUI() 00470 # to complete before continuing. 00471 prefGUI = self.__common.prefGUI() 00472 self.State.setText(state) 00473 operator = self.__common.prefMan().preferences()["operatorobj"] 00474 if self.__common.options().securedir is not None and 'administrator' not in operator.getRoles(): 00475 permissions = operator.getPermissions() 00476 self.cmbOperator.setEnabled(0) 00477 if 'set_particle_type' in permissions: 00478 self.comboParticleType.setEnabled(enabled) 00479 if 'set_orientation' in permissions: 00480 self.comboOrientation.setEnabled(enabled) 00481 if 'set_site_run_condition' in permissions: 00482 prefGUI.comboSite.setEnabled(enabled) 00483 if 'set_instrument_type_run_condition' in permissions: 00484 prefGUI.comboInstrumentType.setEnabled(enabled) 00485 if 'set_phase_run_condition' in permissions: 00486 prefGUI.comboPhase.setEnabled(enabled) 00487 else: 00488 self.cmbOperator.setEnabled(enabled) 00489 self.comboParticleType.setEnabled(enabled) 00490 self.comboOrientation.setEnabled(enabled) 00491 prefGUI.comboSite.setEnabled(enabled) 00492 prefGUI.comboInstrumentType.setEnabled(enabled) 00493 prefGUI.comboPhase.setEnabled(enabled) 00494 00495 def ResetClicked(self): 00496 RunControlMainGUIImpl.ResetClicked(self) 00497 if self.__app.__class__.__name__ == 'userSuite' and not self.__app.isRunning(): 00498 if self.__app.rcSetupSuite() is None: 00499 log.error("Suite setup failed...Aborting suite %s" % self.__app.getName()) 00500 else: 00501 self.execGUImethodNR(self.State.setText, "SUITE SETUP") 00502 else: 00503 self.waitCursor() 00504 self.__actionEngine.spawn(self.__reset) 00505 self.normalCursor() 00506 00507 def __reset(self): 00508 if self.__fsm is not None: 00509 curState = self.__fsm.current_state 00510 if curState == "STOPPED": 00511 self.execGUImethodNR(self.State.setText, "TEARING DOWN") 00512 self.__fsm.process('TEARDOWN') 00513 self.execGUImethodNR(self.handleComments, curState, self.__fsm.current_state) 00514 00515 elif curState == "RUNNING": 00516 self.execGUImethodNR(self.State.setText, "STOPPING RUN") 00517 status = self.__fsm.process('STOP_RUN') 00518 if status is None: 00519 self.execGUImethodNR(self.State.setText, "TEARING DOWN") 00520 self.__fsm.process('TEARDOWN') 00521 self.execGUImethodNR(self.handleComments, curState, self.__fsm.current_state) 00522 00523 elif curState == "PAUSED": 00524 self.execGUImethodNR(self.State.setText, "STOPPING RUN") 00525 self.execGUImethodNR(QToolTip.add, self.PauseButton, "Pause") 00526 self.execGUImethodNR(QToolTip.add, self.RunButton, "Run") 00527 status = self.__fsm.process('STOP') 00528 if status is None: 00529 self.execGUImethodNR(self.State.setText, "TEARING DOWN") 00530 self.__fsm.process('TEARDOWN') 00531 self.execGUImethodNR(self.handleComments, curState, self.__fsm.current_state) 00532 00533 self.execGUImethodNR(self.__updateGUI, self.__fsm.current_state, True) 00534 00535 def StopClicked(self): 00536 if self.__app.__class__.__name__ == 'userSuite': 00537 self.execGUImethodNR(self.State.setText, "STOPPING SUITE") 00538 self.__app.rcStopSuite() 00539 self.execGUImethodNR(self.__updateGUI, "SUITE STOPPED", True) 00540 else: 00541 RunControlMainGUIImpl.StopClicked(self) 00542 00543 self.waitCursor() 00544 self.__actionEngine.spawn(self.__stop) 00545 self.normalCursor() 00546 00547 # New code that spawns StopSuite -- not fully tested 00548 # def StopClicked(self): 00549 # if self.__app.__class__.__name__ == 'userSuite': 00550 # self.__actionEngine.spawn(self.__stopSuite) 00551 # else: 00552 # RunControlMainGUIImpl.StopClicked(self) 00553 # 00554 # self.waitCursor() 00555 # self.__actionEngine.spawn(self.__stop) 00556 # self.normalCursor() 00557 00558 # def __stopSuite(self): 00559 # self.execGUImethodNR(self.State.setText, "STOPPING SUITE") 00560 # self.__app.rcStopSuite() 00561 # self.execGUImethodNR(self.__updateGUI, "SUITE STOPPED", True) 00562 00563 def __stop(self): 00564 if self.__fsm is not None: 00565 curState = self.__fsm.current_state 00566 if curState == "PAUSED": 00567 self.execGUImethodNR(self.State.setText, "STOPPING RUN") 00568 self.execGUImethodNR(QToolTip.add, self.PauseButton, "Pause") 00569 self.execGUImethodNR(QToolTip.add, self.RunButton, "Run") 00570 self.__fsm.process('STOP') 00571 self.execGUImethodNR(self.handleComments, curState, self.__fsm.current_state) 00572 00573 elif curState == "RUNNING": 00574 self.execGUImethodNR(self.State.setText, "STOPPING RUN") 00575 self.__fsm.process('STOP_RUN') 00576 self.execGUImethodNR(self.handleComments, curState, self.__fsm.current_state) 00577 00578 elif curState == "RESET": 00579 self.execGUImethodNR(self.State.setText, "SETTING UP") 00580 self.__fsm.process('SETUP') 00581 self.execGUImethodNR(self.handleComments, curState, self.__fsm.current_state) 00582 00583 self.execGUImethodNR(self.__updateGUI, self.__fsm.current_state, True) 00584 00585 def RunClicked(self): 00586 if self.__app.__class__.__name__ == 'userSuite': 00587 if self.__app.isRunning() and not self.__app.isPaused(): 00588 return 00589 curText = self.execGUImethod(self.State.text) 00590 if self.__app.isPaused(): 00591 text = "RESUMING SUITE" 00592 else: 00593 text = "STARTING SUITE" 00594 self.execGUImethodNR(self.__updateGUI, text, False) 00595 if self.__app.rcStartSuite() is not None: 00596 text = curText 00597 else: 00598 text = "SUITE RUNNING" 00599 self.execGUImethodNR(self.__updateGUI, text, False) 00600 else: 00601 RunControlMainGUIImpl.RunClicked(self) 00602 00603 self.waitCursor() 00604 self.__actionEngine.spawn(self.__run) 00605 self.normalCursor() 00606 00607 def __run(self): 00608 if self.__fsm is not None: 00609 curState = self.__fsm.current_state 00610 if curState == "PAUSED": 00611 self.execGUImethodNR(self.State.setText, "RESUMING RUN") 00612 self.execGUImethodNR(QToolTip.add, self.PauseButton, "Pause") 00613 self.execGUImethodNR(QToolTip.add, self.RunButton, "Run") 00614 self.__fsm.process('RESUME') 00615 00616 elif curState == "STOPPED": 00617 self.execGUImethodNR(self.State.setText, "STARTING RUN") 00618 self.__fsm.process('START_RUN') 00619 00620 elif curState == "RESET": 00621 self.execGUImethodNR(self.State.setText, "SETTING UP") 00622 status = self.__fsm.process('SETUP') 00623 if status is None: 00624 self.execGUImethodNR(self.State.setText, "STARTING RUN") 00625 self.execGUImethodNR(self.State.setText, self.__fsm.current_state) 00626 self.__fsm.process('START_RUN') 00627 self.execGUImethodNR(self.handleComments, curState, self.__fsm.current_state) 00628 00629 self.execGUImethodNR(self.__updateGUI, self.__fsm.current_state, False) 00630 00631 def PauseClicked(self): 00632 if self.__app.__class__.__name__ == 'userSuite': 00633 text = self.execGUImethod(self.State.text) 00634 if self.__app.isPaused(): 00635 return 00636 self.execGUImethodNR(self.State.setText, "PAUSING SUITE") 00637 if self.__app.rcPauseSuite() is None: 00638 self.execGUImethodNR(self.State.setText, "SUITE PAUSED") 00639 else: 00640 self.execGUImethodNR(self.State.setText, text) 00641 else: 00642 RunControlMainGUIImpl.PauseClicked(self) 00643 00644 self.waitCursor() 00645 self.__actionEngine.spawn(self.__pause) 00646 self.normalCursor() 00647 00648 def __pause(self): 00649 if self.__fsm is not None: 00650 curState = self.__fsm.current_state 00651 if curState == "RUNNING": 00652 self.execGUImethodNR(self.State.setText, "PAUSING RUN") 00653 self.execGUImethodNR(QToolTip.add, self.PauseButton, "Resume") 00654 self.execGUImethodNR(QToolTip.add, self.RunButton, "Resume") 00655 self.__fsm.process('PAUSE') 00656 self.execGUImethodNR(self.handleComments, curState, self.__fsm.current_state) 00657 elif curState == "PAUSED": 00658 self.execGUImethodNR(self.State.setText, "RESUMING UP") 00659 self.execGUImethodNR(QToolTip.add, self.PauseButton, "Pause") 00660 self.execGUImethodNR(QToolTip.add, self.RunButton, "Run") 00661 self.__fsm.process('RESUME') 00662 self.execGUImethodNR(self.handleComments, curState, self.__fsm.current_state) 00663 00664 self.execGUImethodNR(self.State.setText, self.__fsm.current_state) 00665 00666 def doReset(self): 00667 """Emit a Reset button click under program control. 00668 Can be done from a subthread.""" 00669 if self.__app.__class__.__name__ == 'userSuite': 00670 if self.__app.isRunning(): 00671 self.doSuiteReset() 00672 self.doEmit(self.ResetButton) 00673 00674 def doSuiteReset(self): 00675 """Notify rcSuite that there was an exception in the suite script 00676 This gives rcSuite a chance to do its own doReset on the running script. 00677 """ 00678 app = self.__app 00679 while app.getApp().__class__.__name__ != 'userApplication': 00680 app = app.getApp() 00681 app.resetSuiteScript() 00682 00683 def doStop(self): 00684 """Emit a Stop button click under program control. 00685 Can be done from a subthread.""" 00686 self.doEmit(self.StopButton) 00687 00688 def doStopRun(self): 00689 """Depricated version of doStop().""" 00690 self.doStop() 00691 00692 def doRun(self): 00693 """Emit a Run button click under program control. 00694 Can be done from a subthread.""" 00695 self.doEmit(self.RunButton) 00696 00697 def doPause(self): 00698 """Emit a Pause button click under program control. 00699 Can be done from a subthread.""" 00700 self.doEmit(self.PauseButton) 00701 00702 def doEmit(self, obj, signal = SIGNAL("clicked()"), args = ()): 00703 """Emit a signal, possibly from a subthread. 00704 By default the signal is 'clicked()' with no arguments.""" 00705 if self.isGUIthread(): 00706 obj.emit(signal, args) 00707 else: 00708 self.execGUImethodNR(obj.emit, signal, args) 00709 00710 def normalCursor(self): 00711 self.execGUImethodNR(self.setCursor, QCursor(QWidget.ArrowCursor)) 00712 00713 def waitCursor(self): 00714 self.execGUImethodNR(self.setCursor, QCursor(QWidget.WaitCursor)) 00715 00716 def handleEndRunDlg(self): 00717 prefs = self.__common.prefMan().preferences() 00718 secMan = self.getSecurityMan() 00719 loginId = prefs["operatorobj"].getLoginId() 00720 self.__endRunDlg.initialize(secMan, loginId) 00721 if self.__endRunDlg.exec_loop() == QDialog.Accepted: 00722 app = self.__app 00723 while app.__class__.__name__ != 'userApplication': 00724 app = app.getApp() 00725 curComplStatusStr = app.getCompletionStatusStr() 00726 (cv, cs) = self.__endRunDlg.getNewCompletionStatus(curComplStatusStr) 00727 if cv is not None: 00728 app.setCompletionStatus(cv) 00729 msg = ("Completion Status '%s' is overridden with status '%s' by operator %s" % 00730 (curComplStatusStr, cs, prefs["operator"])) 00731 log.info(msg) 00732 self.__endRunDlg.enterComment(msg, addToHistory=1) 00733 else: 00734 self.__endRunDlg.clearHistory() 00735 00736 def getEndRunComments(self): 00737 return self.__endRunDlg.comments.getWidget() 00738 00739 def handleComments(self, fromState, toState): 00740 cw = self.getCommentWidget() 00741 if toState == 'STOPPED' and fromState != 'RESET': 00742 if cw.isReadOnly(): 00743 cw.setReadOnly(0) 00744 self.showComments() 00745 cw.enterComment("Comment entry enabled.") 00746 cw.enterComment("Note: Comments entered after this point will apply to the next run",0) 00747 cw.clearHistory() 00748 elif toState == 'RUNNING': 00749 if cw.isReadOnly(): 00750 cw.setReadOnly(0) 00751 self.showComments() 00752 cw.enterComment("Comment entry enabled.") 00753 #if fromState != 'PAUSED' and fromState != 'RESET': 00754 # cw.clearDisplay() 00755 elif toState == 'RESET': 00756 if fromState is None: # Application has been loaded 00757 cw.clearHistory() 00758 cw.setReadOnly(0) 00759 self.showComments() 00760 cw.enterComment("Comment entry enabled.") 00761 else: 00762 if not self.inShutdown: 00763 cw.clearHistory() 00764 cw.setReadOnly(1) 00765 self.hideComments() 00766 cw.enterComment("Comment entry disabled.") 00767 00768 def showHidePythonShell(self): 00769 if self.viewPythonShell.isOn(): 00770 self.showPythonShell() 00771 if self.__fsm is None: 00772 self.getPythonShellWidget().redirectIO() 00773 elif self.__fsm.current_state != 'RUNNING': 00774 self.getPythonShellWidget().redirectIO() 00775 else: 00776 self.hidePythonShell() 00777 if self.__fsm is None: 00778 self.getPythonShellWidget().restoreIO() 00779 elif self.__fsm.current_state == 'RUNNING': 00780 self.getPythonShellWidget().restoreIO() 00781 00782 def verifySoftware(self): 00783 for env in ROOT_DIR_ENVS: 00784 if os.environ.has_key(env): 00785 self.waitCursor() 00786 qApp.processEvents() 00787 (release, failedFiles) = Gversions.verifyFileDigests(rootDirEnv=env) 00788 self.normalCursor() 00789 strFailed = "" 00790 i = 0 00791 for filename in failedFiles: 00792 i += 1 00793 if i % 5 == 0: 00794 strFailed += '\n' 00795 strFailed += filename + ' ' 00796 subsys = env[:env.find("_ROOT")] 00797 if len(failedFiles) == 0: 00798 mb = QMessageBox.information( None, "Software Verification Results", 00799 "%s Release = %s\n\n" % (subsys, release) + 00800 "Verification completed successfully", 00801 QMessageBox.Ok + QMessageBox.Default, 00802 QMessageBox.Cancel) 00803 else: 00804 mb = QMessageBox.warning ( None, "Software Verification Results", 00805 "%s release = %s\n\n" % (subsys, release) + 00806 "Verification found the following " + 00807 "inconsistent files:\n" + strFailed, 00808 QMessageBox.Ok + QMessageBox.Default, 00809 QMessageBox.Cancel) 00810 if mb == QMessageBox.Cancel: 00811 break 00812 00813 def elogWebsite(self): 00814 self.__common.elogWebsite() 00815 00816 def warnFreeSpace(self, warn_level): 00817 mess = "Low Disk Space Warning!\n" + \ 00818 "The space available for data writing\n" + \ 00819 "is below %u Megabytes. Continue?" % (warn_level / (1024*1024)) 00820 mb = QMessageBox.warning(None, '', mess, 00821 QMessageBox.Yes | QMessageBox.Default, 00822 QMessageBox.No | QMessageBox.Escape, 00823 QMessageBox.NoButton ); 00824 return mb is not QMessageBox.No 00825 00826 def warnDirectoryExist(self, directory): 00827 mess = "Directory write error!\n" + \ 00828 "The directory chosen for data writing:\n" + \ 00829 "%s does not exist. Continue?" % (directory) 00830 mb = QMessageBox.warning(None, '', mess, 00831 QMessageBox.Yes | QMessageBox.Default, 00832 QMessageBox.No | QMessageBox.Escape, 00833 QMessageBox.NoButton ); 00834 return mb is not QMessageBox.No 00835 00836 def aboutRunControl(self): 00837 release = Gversions.getReleaseFromDigest() 00838 message = "RunControl System\n\nLATTE Release: %s" % release 00839 if sys.modules.has_key('sihippo'): 00840 message = message + "\n\nHippoDraw Release: %s" % sys.modules['sihippo'].__version__ 00841 message = message + "\n\nQt Version: %s" % qVersion() 00842 message = message + "\n\nPyQt Version: %s" % PYQT_VERSION_STR 00843 if 'SIP_VERSION' in dir(sip): 00844 message = message + "\n\nSIP Version: %s" % hex(sip.SIP_VERSION) 00845 subsysReleases = {} 00846 for subsysRoot in ROOT_DIR_ENVS: 00847 if subsysRoot != 'LATTE_ROOT' and os.environ.has_key(subsysRoot): 00848 release = Gversions.getReleaseFromDigest(digestDataFile=None, rootDirEnv=subsysRoot) 00849 subsysReleases[subsysRoot[:subsysRoot.find("_ROOT")]] = release 00850 if len(subsysReleases) > 0: 00851 message = message + "\n\nSubsystem Releases:" 00852 subSysList = subsysReleases.keys() 00853 subSysList.sort() 00854 for subSys in subSysList: 00855 message = message + "\n %s\t%s" % (subSys, subsysReleases[subSys]) 00856 QMessageBox.about(self,'LATTE RunControl', message) 00857 00858 00859 def fileLogout(self): 00860 if not self.login(): 00861 self.inShutdown = True 00862 self.close() 00863 00864 def closeEvent(self, e): 00865 if self.__common.options().securedir is not None and not self.inShutdown and \ 00866 not self.__common.prefMan().preferences()["operatorobj"].isAdministrator(): 00867 e.ignore() 00868 else: 00869 RunControlMainGUIImpl.closeEvent(self, e) 00870 00871 def login(self): 00872 # Write the modified preferences to the cfg file 00873 loginResult = -1 00874 while loginResult == -1: 00875 loginResult = self.__common.showLogin(self) 00876 if loginResult == -1: 00877 QMessageBox.critical(self, "Authentication Failed", 00878 "Invalid user id or password") 00879 elif loginResult == -2: 00880 QMessageBox.critical(self, "Authentication Failed", 00881 "Password database is not available") 00882 if loginResult == -1: 00883 continue 00884 elif loginResult != 1: 00885 return loginResult 00886 # Set the operator based on login 00887 operator = self.__common.prefMan().preferences()["users"].getUserByLoginId(self.__common.getLoginId()) 00888 if operator is None: 00889 QMessageBox.critical(self, "Authentication Failed", 00890 "Can not find the user under preferences") 00891 log.error("User authentication failed, can not find the user %s under preferences" 00892 % self.__common.getLoginId()) 00893 loginResult = -1 00894 continue 00895 else: 00896 self.cmbOperator.setCurrentText(operator.getName()) 00897 self.cmbOperator.setEnabled(0) 00898 self.__common.prefMan().setOperator(operator.getName()) 00899 self.applyPermissions(operator) 00900 return 1 00901 00902 def getHardwareVersions(self, lat=None, xbrd=None): 00903 # Deprecated. Use the version in RunControlCommon to be 00904 # compatible with standalone scripts and suites. 00905 if self.__hwVersions is None: 00906 if lat is None: lat = self.__common.getLAT() 00907 if xbrd is None: xbrd = self.__common.getXBRD() 00908 self.__hwVersions = Gversions.getHardwareVersions(lat, xbrd) 00909 return self.__hwVersions 00910 00911 def clearHardwareVersionsCache(self): 00912 # Deprecated. Use the version in RunControlCommon to be 00913 # compatible with standalone scripts and suites. 00914 self.__hwVersions = None 00915 00916 def getFSM(self): 00917 return self.__fsm 00918 00919 def getApp(self): 00920 return self.__app 00921 00922 def isGUIthread(self): 00923 """Returns True if this method is being called from the GUI thread. 00924 """ 00925 return threading.currentThread() == self.__guiThread 00926 00927 def customEvent(self, e): 00928 """This method overrides the QObject base class's. There is generally 00929 no reason to call this method directly. It is called by the Qt 00930 infrastructure whenever the custom event is posted, e.g. by the following 00931 three methods of this class: createGUI, execGUImethodNR and execGUImethod. 00932 """ 00933 self.__guiBridge.handleCustomEvent(e) 00934 00935 def createGUI(self, obj, *args, **kwargs): 00936 """Method used for instantiating a GUI from a non-GUI subthread""" 00937 return self.__guiBridge.createGUI(self, obj, *args, **kwargs) 00938 00939 def execGUImethodNR(self, func, *args, **kwargs): 00940 """Method used for executing a GUI function from a non-GUI thread. 00941 Use this method when no (useful) response is expected or 00942 when waiting for one could cause a deadlock. Any response from the 00943 function is lost.""" 00944 if self.inShutdown: return # Don't try to update the GUI during shutdown 00945 00946 return self.__guiBridge.execGUImethodNR(self, func, *args, **kwargs) 00947 00948 def execGUImethod(self, func, *args, **kwargs): 00949 """Method used for executing a GUI function from a non-GUI thread. 00950 Use this method when a response is expected from the function and 00951 when it is appropriate to wait for it (no deadlock arises)""" 00952 if self.inShutdown: return # Don't try to update the GUI during shutdown 00953 00954 return self.__guiBridge.execGUImethod(self, func, *args, **kwargs) 00955 00956 def getSecurityMan(self): 00957 return self.__common.getSecurityMan() 00958