seAppBase.py

Go to the documentation of this file.
00001 #!/usr/local/bin/python
00002 #
00003 #                               Copyright 2005
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__ = "Script engine application base class"
00012 __author__   = "S. Tuvi <stuvi@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online"
00013 __date__     = "2005/07/15 00:08:27"
00014 __updated__  = "$Date: 2006/04/26 21:52:26 $"
00015 __version__  = "$Revision: 1.26 $"
00016 __release__  = "$Name: HEAD $"
00017 __credits__  = "SLAC"
00018 
00019 import LICOS.copyright_SLAC
00020 
00021 import logging as log
00022 import os
00023 import time
00024 import types
00025 
00026 # The following imports are needed for added LATTE4 features
00027 from   threading import Thread, Event, Lock
00028 
00029 from LICOS.scriptEngine.FSM                import FSM
00030 from LICOS.scriptEngine.ScriptEngineCommon import ScriptEngineCommon
00031 from LICOS.scriptEngine                    import rcUtil, rcActivitiesInterface
00032 
00033 from LICOS.scriptEngine   import rcReportGen, rcRunIdHelper, rcComplStatus
00034 from LICOS.lib.testReport import rcTestReport
00035 
00036 from   LICOS.util             import FreeSpace
00037 from   LICOS.core.export      import DataExport
00038 from   LICOS.util.gVersions   import Gversions, ROOT_DIRS
00039 from   LICOS.lib.LATconstants import *
00040 
00041 
00042 class seAppBase(FSM):
00043   """!\brief Base class for all script engine tests.
00044 
00045   All script engine test applications should inherit from this base class.
00046   This class in turn inherits from the FSM (a finite state machine) class
00047   which allows the scriptRun method to go through its transitions and
00048   execute the test.
00049 
00050   getRunSequence() method notifies the script engine as to the order of the
00051   transitions to be processed. If any of the transition methods return a
00052   value other than None, then the transition is rejected and the test is
00053   put back to the RESET state.
00054 
00055 
00056   """
00057   def __init__(self, common):
00058     """!\brief seAppBase constructor.
00059 
00060     \param common ScriptEngineCommon instance.
00061                   Contains session helper methods such as
00062                   preferences, command line options, etc.
00063 
00064     """
00065     FSM.__init__(self, 'RESET', None)
00066     self.set_default_transition(                            self.error,     None    )
00067     self.add_transition        ('SETUP',     'RESET',       self.seSetup,   'STOPPED')
00068     self.add_transition        ('START_RUN', 'STOPPED',     self.seStartRun,'RUNNING')
00069     self.add_transition        ('RUN',       'RUNNING',     self.seRun,     'COMPLETE')
00070     self.add_transition        ('STOP_RUN',  'COMPLETE',    self.seStopRun, 'STOPPED')
00071     self.add_transition        ('ABORT',     'RUNNING',     self.seAbort,   'STOPPED')
00072     self.add_transition        ('TEARDOWN',  'STOPPED',     self.seTeardown,'RESET')
00073 
00074     self.common = common
00075     self.gui = common.getGUI()
00076     self.rc = self.gui # for backward compatibility
00077     self.__stopRequested = False
00078 
00079     # The following are needed for added LATTE4 features
00080     self.__complStatus         = rcComplStatus.rcCompletionStatus()
00081 
00082     self.prefs                 = self.common.preferences()
00083     self.vsc                   = self.common.getCmdCli()
00084 
00085     self.__archiver            = None
00086     self.__archiverClass       = None
00087     self.__errArchiver         = None
00088     self.__badArchiver         = None
00089 
00090     self.__pauseCount          = 0
00091     self.__pausedTime          = 0
00092     self.__pauseTime           = None
00093     self.__startTime           = None
00094     self.__strobeTime          = None
00095     self.__elapsedTime         = 0
00096     self.__endTime             = None
00097     self.__excludedTime        = 0
00098     self.__excludeTime         = 0
00099     self.__excluding           = False
00100     self.__excludeLock         = Lock()
00101     self.__tstamp              = None
00102     self.__tstampRaw           = None
00103     self.__statMon             = None
00104 
00105     # This will contain the parent suite instance during execution
00106     self.__parentSuite         = None
00107 
00108     # This will be True if it is the last script in the suite
00109     self.__lastSuiteScript     = False
00110 
00111     self.__EBFplayback         = self.common.options().playback is not None
00112 
00113     self.__runIdHelper         = rcRunIdHelper.rcRunIdHelper(self.prefs)
00114     self.runId                 = 'N/A'
00115 
00116     self.__customReportData    = {}
00117 
00118     if self.gui is not None:
00119       self.__paramVerifier       = self.gui.parameterVerifier()
00120 
00121     self.__verifyStatus        = None
00122 
00123     self.__appStatus           = None
00124     self.__completionStatus    = self.COMPL_STATUS_UNDEFINED
00125 
00126     self.__dds               = self.common.getDDS()
00127 
00128     # Contains the full path for the run based message log
00129     self.__runMsgLogFile = None
00130 
00131     # Contains the full path for the run based LCMMSGOUT log
00132     self.__runLcmLogFile = None
00133 
00134     # Additional input files
00135     self.__additionalInputFiles = []
00136 
00137     if self.gui is not None and 'statusMonitor' in dir(self.gui):
00138       statMon = self.gui.statusMonitor()
00139       statMon.addWatchItem('Run Id', self.getRunId)
00140       statMon.addWatchItem('Test Name', self.getName)
00141       statMon.addWatchItem('Suite Name', self.getParentSuiteName)
00142       statMon.addWatchItem('Additional Input Files', self.getAddlInputFiles)
00143       statMon.addWatchItem('Completion Status', self.getCompletionStatusStr,
00144                             alarmExpr="@!='" + self.__complStatus.getCompletionStatusStr(self.COMPL_STATUS_PASSED)+"'")
00145       statMon.addWatchItem('Start Time', self.getStartTime)
00146       statMon.addWatchItem('End Time', self.getEndTime)
00147       statMon.addWatchItem('Elapsed Time', self.getElapsedTime)
00148       statMon.addWatchItem('Run Message Log File', self.getRunMsgLogFile)
00149       statMon.addWatchItem('Error Log Count', self.common.getErrorLogCount, alarmExpr='!=0')
00150       statMon.addWatchItem('Free Disk Space', FreeSpace.FreeSpace,
00151                            alarmExpr='<'+ str(self.DISKSPACE_WARN_LEVEL))
00152       statMon.addWatchItem('Suite State', self.getSuiteState)
00153       statMon.addWatchItem('Application State', self.getAppState)
00154       self.__statMon = statMon
00155 
00156     self.__reportGen = rcReportGen.rcReportGen(os.path.join(self.prefs["reportdir"], 'rcReport.out'), self.prefs)
00157     try:
00158       if self.prefs is not None and int(self.prefs["elognbl"]):
00159         self.__activitiesObj = rcActivitiesInterface.ActivitiesInterface()
00160       else:
00161         self.__activitiesObj = None
00162     except:
00163       self.__activitiesObj = None
00164 
00165 
00166   def getRunSequence(self):
00167     """!\brief Return the order of transitions for this type of application.
00168 
00169     \return A list of transitions
00170     """
00171     return ['SETUP','START_RUN','RUN','STOP_RUN','TEARDOWN']
00172 
00173   def doAbort(self):
00174     """!\brief Request an abort.
00175 
00176     """
00177     if self.get_transition("ABORT", self.current_state)[1] is not None:
00178       return self.process("ABORT")
00179 
00180   def doReset(self):
00181     """!\brief Reset the application.
00182 
00183     """
00184     curState = self.current_state
00185     if curState == "STOPPED":
00186       self.process("TEARDOWN")
00187     elif curState in ("RUNNING", "PAUSED"):
00188       status = self.doAbort()
00189       if status is None:
00190         self.process("TEARDOWN")
00191     self.__updateGUI()
00192 
00193   def error(self):
00194     """!\brief This method is called when an undefined transition is requested.
00195 
00196     """
00197     log.error(self.__class__.__name__ + " - Undefined transition: %s from state %s" % \
00198             (self.input_symbol, self.current_state))
00199 
00200   def getSuiteState(self):
00201     suite = self.getParentSuite()
00202     if suite is not None:
00203       return suite.current_state
00204 
00205   def getAppState(self):
00206     return self.current_state
00207 
00208   def getActivitiesObject(self):
00209     """\brief Return the activities table interface
00210 
00211     """
00212     return self.__activitiesObj
00213 
00214   def seSetup(self):
00215     """!\brief SETUP transition method.
00216 
00217     Calls self.setup()
00218 
00219     \return None for success or
00220             False if there is an exception or
00221             status returned by the user script.
00222     """
00223     fn = "seAppBase.seSetup"
00224     log.debug("%s -- %s" % (fn, self.getName()))
00225     self.__additionalInputFiles = []
00226     self.__updateGUI()
00227 
00228     # Call the application back
00229     try:
00230       status = self.setup()
00231       if status is not None:  return status
00232     except:
00233       log.exception("Exception in " + self.getName() + ".setup()")
00234       self.__appStatus = self.COMPL_STATUS_FAILED
00235       return False                      # Stay in RESET state
00236 
00237   def seTeardown(self):
00238     """!\brief TEARDOWN transition method.
00239 
00240     Calls self.teardown()
00241 
00242     \return None for success or status returned by the user script.
00243     """
00244     fn = "seAppBase.seTeardown"
00245     log.debug("%s -- %s" % (fn, self.getName()))
00246     self.__updateGUI()
00247 
00248     # Just in case this was missed in __stopRun because of an exception...
00249     self.__strobeTime = None
00250 
00251     # Call the application back
00252     try:
00253       status = self.teardown()
00254     except:                             # Continue as if nothing had happened
00255       log.exception("Exception in " + self.getName() + ".teardown()")
00256       self.__appStatus = self.COMPL_STATUS_FAILED
00257       status = None                     # Go into RESET state
00258     return status
00259 
00260   def seStartRun(self):
00261     """!\brief START_RUN transition method
00262 
00263     Calls self.startRun()
00264 
00265     \return None for success or
00266             False if there is an exception or
00267             status returned by the user script.
00268     """
00269     fn = "seAppBase.seStartRun"
00270     log.debug("%s -- %s" % (fn, self.getName()))
00271 
00272     self.__updateGUI()
00273     self.__stopRequested = False
00274 
00275     self.__appStatus        = None
00276     self.__completionStatus = self.COMPL_STATUS_UNDEFINED
00277     self.__endTime          = None
00278     self.__startTime        = None
00279     self.__excludeLock.acquire()
00280     self.__elapsedTime      = 0
00281     self.__strobeTime       = None
00282     self.__excluding        = False
00283     self.__excludedTime     = 0
00284     self.__excludeLock.release()
00285     self.__pauseCount       = 0
00286     self.__pausedTime       = 0
00287 
00288     self.common.resetErrorLogCount()
00289 
00290     # This while loop makes sure the session log level filename is different
00291     # than the run level filename by waiting 1 second if necessary.
00292     # This condition may happen in standalone scripts and suites.
00293     while True:
00294       # The timestamp is measured here because things below depend on its value.
00295       # Therefore the run time includes the amount of time spent in the parameter verifier
00296       # in addition to the time the user spends entering the parameters.
00297       self.__tstampRaw   = time.gmtime()
00298       self.__tstamp      = time.strftime('%y%m%d%H%M%S', self.__tstampRaw)
00299       if self.prefs.has_key("logfilename"):
00300         logfilename = self.prefs["logfilename"]
00301         if self.__tstamp != logfilename[3:15]:
00302           break
00303       else:
00304         break
00305       time.sleep(1)
00306 
00307     self.__exportedFiles      = []
00308 
00309     # Contains the rcTestReport instance used by the script if any
00310     self.__testReport = None
00311     # Contains the file name for the test report generated by the script
00312     self.__testReportFile = None
00313 
00314     self.runId = self.__runIdHelper.nextRunId()
00315 
00316     # Check exported directory, as per LTE-226
00317     if int(self.prefs["dataexport"]) == 1:
00318       exportDir = os.path.abspath(self.prefs["exportdir"])
00319       if not os.path.exists(exportDir):
00320         msg = "Export directory %s does not exist.  Aborting run" % exportDir
00321         log.fatal(msg)
00322         return False    # reject the transition
00323 
00324     if self.gui is not None:
00325       self.__paramVerifier.initialize(self.getName())
00326     self.__verifyStatus = None
00327 
00328     now = time.time()
00329 
00330     #Initialize timers
00331     self.__endTime     = None
00332     self.__startTime   = now
00333     self.__excludeLock.acquire()
00334     self.__elapsedTime = 0
00335     self.__strobeTime  = now
00336     self.__excludeLock.release()
00337 
00338     try:
00339       if self.prefs is not None and int(self.prefs["elognbl"]):
00340         self.__genActivity()
00341     except Exception, e:
00342       log.exception(e)
00343       raise
00344 
00345 
00346     # Call the application back
00347     try:
00348       status = self.startRun()
00349     except:
00350       log.exception("Exception in " + self.getName() + ".startRun()")
00351       self.__appStatus = self.COMPL_STATUS_FAILED
00352       status = False                    # Stay in STOPPED state
00353 
00354     if self.common.options().securedir is not None or self.common.options().paramverify is not None:
00355       # Don't prompt the parameter verifier if the script
00356       # is in a suite that is called from another suite or
00357       # if it is not the first script in the suite
00358       scriptSeq = 0
00359       if self.isRunFromSuite():
00360         suiteInst = self.getParentSuite()
00361         if suiteInst.getParentSuite() is None:
00362           scriptSeq = suiteInst.getScriptSequence()
00363         else:
00364           scriptSeq = -1
00365       if scriptSeq == 0 or scriptSeq == 1:
00366         self.__paramVerifier.add("Particle Type", self.prefs["lastparticletype"])
00367         self.__paramVerifier.add("Orientation", self.prefs["lastorientation"])
00368         self.__paramVerifier.add("Site", self.prefs["lastsite"])
00369         self.__paramVerifier.add("Instrument Type", self.prefs["lastinstrumenttype"])
00370         self.__paramVerifier.add("Phase", self.prefs["lastphase"])
00371       self.__verifyStatus = self.gui.execGUImethod(self.__paramVerifier.verify)
00372       if self.__verifyStatus == 1: # The user successfully signed off on the parameters
00373         pass
00374       elif self.__verifyStatus == 0: # The user cancelled the verification
00375         log.error("Parameter verification failed because the user cancelled verification.")
00376         return self.__verifyStatus
00377       else:
00378         if self.__verifyStatus is None: self.__verifyStatus = -1
00379         log.error("Parameter verification failed due to an unknown reason.")
00380         return self.__verifyStatus
00381 
00382     self.__runMsgLogFile = None
00383     self.__runLcmLogFile = None
00384     if int(self.prefs["lognbl"]) == 1:
00385       self.__runMsgLogFile = os.path.join(self.prefs["logdir"],
00386                                           "msg" + self.__tstamp + ".log")
00387       self.__runMsgLog = self.common.startRunLog(self.__runMsgLogFile)
00388       self.__runLcmLogFile = os.path.join(self.prefs["logdir"],
00389                                           "lcm" + self.__tstamp + ".log")
00390       self.common.startLcmLog(self.__runLcmLogFile)
00391     log.info("""%s -- Test %s starting run, Operator: %s,"""
00392              """ Run Id: %s, Time Started: %s -- %s"""
00393               % (fn, self.getName(), self.prefs["operator"],
00394                  self.runId, self.__tstamp, time.asctime(self.__tstampRaw)))
00395     return status
00396 
00397   def seRun(self):
00398     """!\brief RUN transition method
00399 
00400     Calls self.run()
00401 
00402     \return None for success or
00403             False if there is an exception or
00404             status returned by the user script.
00405     """
00406     fn = "seAppBase.seStartRun"
00407     log.debug("%s -- %s" % (fn, self.getName()))
00408 
00409     self.__updateGUI()
00410     try:
00411       status = self.__jacket(self.run)
00412     except:
00413       log.exception("Exception in " + self.getName() + ".startRun()")
00414       self.__appStatus = self.COMPL_STATUS_FAILED
00415       self.doReset()
00416       status = False
00417     return status
00418 
00419   def seStopRun(self):
00420     """!\brief STOP_RUN transition method
00421 
00422     Calls self.stopRun()
00423     """
00424     fn = "seAppBase.seStopRun"
00425     log.debug("%s -- %s" % (fn, self.getName()))
00426     self.__updateGUI()
00427 
00428     # Call the application back
00429     try:
00430       self.stopRun()       # RiC: It appears rejecting the transition won't work
00431     except:                             # Go into STOPPED state
00432       log.exception("Exception in " + self.getName() + ".stopRun()")
00433       self.__appStatus = self.COMPL_STATUS_FAILED
00434 
00435     # Stop the run - Must be done after the application stopRun()
00436     self.__stopRun()
00437     self.__logEndRun(fn)
00438     curDir = os.getcwd()
00439     self.__exportData()
00440     if os.path.exists(curDir):
00441       os.chdir(curDir)
00442 
00443   def seAbort(self):
00444     """!\brief ABORT transition method
00445 
00446     Calls self.abort()
00447 
00448     \return None for success or
00449             False if there is an exception or
00450             status returned by the user script.
00451     """
00452     fn = "seAppBase.seAbort"
00453     log.debug("%s -- %s" % (fn, self.getName()))
00454 
00455     self.__updateGUI()
00456     self.setStopRequested()
00457     return self.abort()
00458 
00459   def __updateGUI(self):
00460     """!\brief Update the state of the run on the GUI.
00461 
00462     \param  transition Transition string such as ('SETUP', 'START_RUN')
00463     """
00464     if self.gui is not None:
00465       self.gui.execGUImethodNR(self.gui.updateState, self.current_state)
00466 
00467   def setup(self):
00468     """ \brief Transition method to be overridden by the user script.
00469 
00470     """
00471     pass
00472 
00473   def teardown(self):
00474     """ \brief Transition method to be overridden by the user script.
00475 
00476     """
00477     pass
00478 
00479   def startRun(self):
00480     """ \brief Transition method to be overridden by the user script.
00481 
00482     """
00483     pass
00484 
00485   def run(self):
00486     """ \brief Transition method to be overridden by the user script.
00487 
00488     """
00489     pass
00490 
00491   def stopRun(self):
00492     """ \brief Transition method to be overridden by the user script.
00493 
00494     """
00495     pass
00496 
00497   def abort(self):
00498     """ \brief Transition method to be overridden by the user script.
00499 
00500     """
00501     pass
00502 
00503   def getName(self):
00504     """!\brief Return the test application name
00505 
00506     \return Test application class name.
00507     """
00508     return self.__class__.__name__
00509 
00510   def getType(self):
00511     """!\brief Return the type of test based on this class.
00512 
00513     Used for logging purposes.
00514 
00515     \return Test type
00516     """
00517     return 'Application'
00518 
00519   def isStopRequested(self):
00520     """!\brief Return stop requested flag.
00521 
00522     \return Stop requested flag.
00523     """
00524     return self.__stopRequested
00525 
00526   def setStopRequested(self):
00527     """!\brief Signal stop requested.
00528 
00529     """
00530     self.__stopRequested = True
00531 
00532   def setParentSuite(self, suite):
00533     """!\brief Set the parent suite instance.
00534 
00535     \param Suite instance
00536     """
00537     self.__parentSuite = suite
00538 
00539   def getParentSuite(self):
00540     """!\brief Retrieve the parent suite instance.
00541 
00542     \return Suite instance
00543     """
00544     return self.__parentSuite
00545 
00546   def getParentSuiteName(self):
00547     """!\brief Retrieve the parent suite name.
00548 
00549     \return Parent suite name.
00550     """
00551     if self.__parentSuite is not None:
00552       return self.__parentSuite.getName()
00553 
00554   def isRunFromSuite(self):
00555     """!\brief Check if the script is being run from a suite.
00556 
00557     \return True  If the script is being run from a suite,
00558             False otherwise.
00559     """
00560     return self.__parentSuite is not None
00561 
00562   def setLastSuiteScript(self):
00563     """!\brief Set the script as the last one in the suite.
00564 
00565     Called from rcSuite.
00566     """
00567     self.__lastSuiteScript = True
00568 
00569   def isLastSuiteScript(self):
00570     """!\brief Check if the script is the last one.
00571 
00572     """
00573     return self.__lastSuiteScript
00574 
00575   class StandaloneLauncher(object):
00576     """!\brief Class to facilitate the running of the test scripts and
00577     suites in standalone mode.
00578 
00579     This class allows all tests to be run unmodified in standalone
00580     mode. Everything is available in standalone mode except the
00581     GUI object (ie. self.common().getGUI() will be None)
00582 
00583     See testRunner
00584     """
00585     def __init__(self, logLevel="INFO"):
00586       """!\brief StandaloneLauncher constructor.
00587 
00588       \param logLevel Initial loglevel.
00589       """
00590       self.__common = ScriptEngineCommon()
00591       self.__common.initialize()
00592       self.__common.setLoggingLevel(logLevel)
00593       self.__common.connect()
00594 
00595     def launch(self, appClass, callbacks=None):
00596       """!\brief Launch the specified application script.
00597 
00598       This method launches the application passed in \a appClass or
00599       if the \a appClass parameter is None the application whose name
00600       is passed as a parameter in the --app command line argument.
00601 
00602       \param appClass Application instance.
00603       \param callbacks Not implemented yet.
00604 
00605       \return Result from rcUtil.scriptRun()
00606       """
00607       if appClass is None:
00608         module = self.__common.options().app
00609         (module, filename, app) = rcUtil.scriptLoad(module, self.__common)
00610         if app is None:
00611           return
00612         else:
00613           return rcUtil.scriptRun(app)
00614       else:
00615         app= appClass(self.__common)
00616         return rcUtil.scriptRun(app)
00617 
00618     def getCommon(self):
00619       """!\brief Accessor method for the testRunner script
00620 
00621       \return ScriptEngineCommon instance
00622       """
00623       return self.__common
00624 
00625 # The following are needed for added LATTE4 features
00626   COMPL_STATUS_UNDEFINED            = \
00627       rcComplStatus.rcCompletionStatus.getCompletionStatus('UNDEFINED')
00628   COMPL_STATUS_ABORTED              = \
00629       rcComplStatus.rcCompletionStatus.getCompletionStatus('ABORTED')
00630   COMPL_STATUS_ERROR                = \
00631       rcComplStatus.rcCompletionStatus.getCompletionStatus('FAILED')
00632   COMPL_STATUS_SUCCESS              = \
00633       rcComplStatus.rcCompletionStatus.getCompletionStatus('PASSED')
00634   COMPL_STATUS_FAILED               = \
00635       rcComplStatus.rcCompletionStatus.getCompletionStatus('FAILED')
00636   COMPL_STATUS_PASSED               = \
00637       rcComplStatus.rcCompletionStatus.getCompletionStatus('PASSED')
00638   COMPL_STATUS_PASSED_CONDITIONALLY = \
00639       rcComplStatus.rcCompletionStatus.getCompletionStatus('PASSED CONDITIONALLY')
00640   COMPL_STATUS_MARGINAL             = \
00641       rcComplStatus.rcCompletionStatus.getCompletionStatus('MARGINAL')
00642   COMPL_STATUS_OPERATOR_ABORTED = \
00643       rcComplStatus.rcCompletionStatus.getCompletionStatus('OPERATOR ABORTED')
00644   COMPL_STATUS_INCOMPLETE = \
00645       rcComplStatus.rcCompletionStatus.getCompletionStatus('INCOMPLETE')
00646 
00647   # Watermark for diskspace warning
00648   DISKSPACE_WARN_LEVEL = (200*1024*1024)
00649 
00650   def getRunId(self):
00651     """!\brief Return the run id
00652 
00653     \return Run id
00654     """
00655     return self.runId
00656 
00657   def getAddlInputFiles(self):
00658     """!\brief Return a list containing the additional input files.
00659 
00660     """
00661     return self.__additionalInputFiles
00662 
00663   def addAddlInputFile(self, filename):
00664     """!\brief Add an additional input file.
00665 
00666     \param filename Name of the file
00667     """
00668     self.addExportedFile(filename, delete=False)
00669     self.__additionalInputFiles.append(os.path.split(filename)[1])
00670 
00671 
00672   def getRunMsgLogFile(self):
00673     """!\brief Return the filename for the message log for the current run.
00674 
00675     \return Message log filename.
00676     """
00677     if self.__runMsgLogFile is not None:
00678       return os.path.basename(self.__runMsgLogFile)
00679     else:
00680       return None
00681 
00682   def getRunLcmLogFile(self):
00683     """!\brief Return the filename for the LCM log for the current run.
00684 
00685     \return LCM log filename.
00686     """
00687     if self.__runLcmLogFile is not None:
00688       return os.path.basename(self.__runLcmLogFile)
00689     else:
00690       return None
00691 
00692   def getElapsedTime(self):
00693     """!\brief Return the elapsed time spent during test execution.
00694 
00695     \return Elapsed time.
00696     """
00697     return "%.2f seconds"  % self.__getElapsedTime()
00698 
00699   def __getElapsedTime(self):
00700     """!\brief Return the elapsed time (raw)
00701 
00702     \return Elapsed time.
00703     """
00704     self.__excludeLock.acquire()
00705     if self.__strobeTime is not None and \
00706        self.__pauseTime  is     None and not self.__excluding:
00707       elapsedTime = self.__elapsedTime + (time.time() - self.__strobeTime)
00708     else:
00709       elapsedTime = self.__elapsedTime
00710     self.__excludeLock.release()
00711     return elapsedTime
00712 
00713   def getStartTime(self):
00714     """!\brief Return the test start time
00715 
00716     \return Test start time in GMT as a tuple
00717     """
00718     if self.__startTime is not None:
00719       return time.asctime(time.gmtime(self.__startTime))
00720     else:
00721       return None
00722 
00723   def getEndTime(self):
00724     """!\brief Return the test end time
00725 
00726     \return Test end time in GMT as a tuple
00727     """
00728     if self.__endTime is not None:
00729       return time.asctime(time.gmtime(self.__endTime))
00730     else:
00731       return None
00732 
00733   def getDataDistributor(self):
00734     """!\brief Return the data distribution server instance.
00735 
00736     \return Data distribution server instance.
00737     """
00738     return self.__dds
00739 
00740   def setSessionVar(self, key, value):
00741     """!\brief Set session variable \a key to value \a value.
00742 
00743     Session variables are data that is shared between different runs.
00744     They exist as long as the run control session they are defined in.
00745     Typical uses of session variables are to pass data from one suite
00746     script to another or to maintain state during a multiple step
00747     test run.
00748 
00749     \param key   Session variable identifier.
00750     \param value Session variable value.
00751     """
00752     sessionVars = self.common.getDBN()
00753     if sessionVars is not None:
00754       sessionVars[key] = value
00755     else:
00756       raise RuntimeError, "Database node does not exist"
00757 
00758   def getSessionVar(self, key):
00759     """!\brief Return the session variable identified by \a key.
00760 
00761     \param key Session variable identifier.
00762 
00763     \return Value of the session variable if it exists,
00764             otherwise None.
00765     """
00766     sessionVars = self.common.getDBN()
00767     if sessionVars is not None and sessionVars.has_key(key):
00768       return sessionVars[key]
00769     else:
00770       return None
00771 
00772   def setPrefs(self, prefs):
00773     """!\brief Explicitly set preferences.
00774 
00775     When scripts are run in standalone mode it may be
00776     necessary to pass in a script defined set of preferences.
00777 
00778     The preferences specified in \a prefs get appended to
00779     the set of preferences read from the configuration file.
00780 
00781     \param prefs Preferences as a dictionary.
00782     """
00783     for (key, val) in prefs.items():
00784       self.prefs[key] = val
00785 
00786   def setCompletionStatus(self, completionStatus, completionStatusStr=None):
00787     """!\brief Sets the completion status of the test.
00788 
00789     Please use one of the following:
00790 
00791       - COMPL_STATUS_ABORTED              = -2
00792       - COMPL_STATUS_ERROR                = -3
00793       - COMPL_STATUS_FAILED               = -3
00794       - COMPL_STATUS_SUCCESS              =  0
00795       - COMPL_STATUS_PASSED               =  0
00796       - COMPL_STATUS_PASSED_CONDITIONALLY = -4
00797       - COMPL_STATUS_MARGINAL             = -5
00798 
00799     Or a user defined completion status in which case
00800     a description needs to be supplied along with the code.
00801 
00802     \param completionStatus    Numeric completion status code
00803     \param completionStatusStr Completion status description
00804 
00805     """
00806     if not self.__complStatus.isCompletionStatus(completionStatus):
00807       if completionStatusStr is None:
00808         raise RuntimeError, "Need to specify a completion status description " \
00809                             "when specifying a user defined completion status"
00810       else:
00811         addRes = self.__complStatus.addCompletionStatus(completionStatus, completionStatusStr)
00812         if addRes == -2:
00813           raise RuntimeError, "Completion status description '%s' already exists " \
00814                               "under a different status code." % completionStatusStr
00815     self.__completionStatus = completionStatus
00816 
00817   def getCompletionStatus(self):
00818     """!\brief Return the completion status code.
00819 
00820     Returns the code set by the script or
00821     COMPL_STATUS_UNDEFINED if it hasn't been set.
00822 
00823     \return Completion status code.
00824     """
00825     if self.__appStatus is None:
00826       return self.__completionStatus
00827     else:
00828       return self.__appStatus
00829 
00830   def getCompletionStatusStr(self):
00831     """!\brief Return the description of the completion status.
00832 
00833     Returns the code as set by the script or
00834     'UNDEFINED' if it hasn't been set.
00835 
00836     \return Completion status description.
00837     """
00838     completionStatus = self.getCompletionStatus()
00839     return self.__complStatus.getCompletionStatusStr(completionStatus)
00840     #if completionStatus == rcTransitions.COMPL_STATUS_ABORTED:
00841     #  return("ABORTED")
00842     #elif completionStatus == rcTransitions.COMPL_STATUS_ERROR:
00843     #  return("ERROR")
00844     #elif completionStatus == rcTransitions.COMPL_STATUS_SUCCESS:
00845     #  return("SUCCESS")
00846     #else:
00847     #  return("UNDEFINED")
00848 
00849 #  def __isTestComplete(self):
00850 #    """!\brief Check if the criteria for completeness of test is met.
00851 
00852 #    \return Boolean indicating whether the test is complete or not.
00853 #    """
00854 #    if self.common.options().securedir is not None:
00855 #      requiredOptions = ['snapnbl', 'datasave']
00856 #      complete = True
00857 #      for option in requiredOptions:
00858 #        if self.prefs[option] != 1:
00859 #          complete = False
00860 #          break
00861 #      return complete
00862 #    else:
00863 #      return True
00864 
00865   def addCustomReportData(self, key, value):
00866     """!\brief Add custom report data to the run report.
00867 
00868     This method allows the application to add any additional data
00869     that Run Control is not already providing to the run report
00870     (rcReport.out).
00871 
00872     When the custom report data gets processed and written to the
00873     database, it will go under the "AdditionFields" column. Please
00874     only provide values that can be converted to a string.
00875 
00876     \param key   Custom report data identifier.
00877     \param value Custom report data value.
00878     """
00879     self.__customReportData[key] = value
00880 
00881   def getParameterVerifier(self):
00882     """!\brief Return the parameter verifier instance
00883 
00884     \return Parameter verifier instance
00885     """
00886     return self.__paramVerifier
00887 
00888   def addExportedFile(self,file=None,delete=False):
00889     """!\brief Add file to the list of files to be exported.
00890 
00891     \param file   file to be added
00892     \param delete delete original on export (default False)
00893     """
00894     if file is not None:
00895       (filePath,fileName) = os.path.split(file)
00896       self.__exportedFiles.append( [filePath,fileName,delete] )
00897 
00898       # Try to extract the report file from the rcTestReport instance
00899       # that the script may have been using.
00900       if self.__testReport is None:
00901         for objStr in dir(self):
00902           obj = getattr(self, objStr)
00903           if '__class__' in dir(obj):
00904             if rcTestReport.rcTestReport in obj.__class__.__mro__:
00905               self.__testReport = obj
00906               break
00907       if self.__testReport is not None and file in self.__testReport.getReports():
00908         self.__testReportFile = fileName
00909 
00910 
00911   def getFileTimeStamp(self):
00912     """!\brief Return the formatted timestamp used to name output files.
00913 
00914     Using this method, applications can request the formatted timestamp
00915     value used in naming archive, snapshot and housekeeping files.
00916     The format is YYMMDDHHMISS.
00917 
00918     \return Formatted GMT timestamp
00919     """
00920     return self.__tstamp
00921 
00922   def getRawTimeStamp(self):
00923     """!\brief Return the raw timestamp as returned from gmtime().
00924 
00925     \return Raw GMT timestamp tuple.
00926     """
00927     return self.__tstampRaw
00928 
00929   def __genActivity(self):
00930     """!\brief generate the activity db entry
00931 
00932     Called from sestartRun
00933     """
00934 
00935     # Note, JHP: Called at end of run because elogReport table isn't updated til then.
00936     if self.__activitiesObj is not None:
00937       aObj = self.__activitiesObj
00938       suiteID = None
00939       if self.getParentSuite() is not None:
00940         suiteID = self.getParentSuite().getSuiteID()
00941       aObj.newActivity(self.runId, suiteID)
00942       aObj.setState(self.runId, aObj.CommandingState, aObj.CommandingStarted)
00943       aObj.setTime(self.runId, aObj.DataStart, self.__startTime)
00944     else:
00945       log.debug("Activities logging disabled due to prior exception")
00946 
00947   def __endActivity(self):
00948     """!\brief finalize the activities database entry
00949 
00950     Called from __genReport
00951     """
00952     if self.__activitiesObj is not None:
00953       aObj = self.__activitiesObj
00954       aObj.setState(self.runId, aObj.CommandingState, aObj.CommandingComplete)
00955       aObj.setTime(self.runId, aObj.DataStop, self.__endTime)
00956     else:
00957       log.debug("Activities logging disabled due to prior exception")
00958 
00959   def __genReport(self):
00960     """!\brief Generate the run report
00961 
00962     Called through the GUI bridge
00963     """
00964     log.debug("Generating run report...")
00965     self.__reportGen.initialize(self.__tstampRaw)
00966     self.__reportGen.addField("TestName", self.getName())
00967     if self.getParentSuiteName() is not None:
00968       self.__reportGen.addField("SuiteName", self.getParentSuiteName())
00969     self.__reportGen.addField("RunId", self.runId)
00970     if self.prefs.has_key("operator"):
00971       self.__reportGen.addField("Operator", self.prefs["operator"])
00972     if self.prefs.has_key("userid"):
00973       self.__reportGen.addField("OperatorId", self.prefs["userid"], rcReportGen.UNITS_LONG)
00974     self.__reportGen.addField("PauseCount", str(self.__pauseCount), rcReportGen.UNITS_INT)
00975     self.__reportGen.addField("PausedTime", str(self.__pausedTime), rcReportGen.UNITS_FLOAT)
00976     self.__reportGen.addField("StartTime", str(time.gmtime(self.__startTime)))
00977     self.__reportGen.addField("ElapsedTime", str(self.__elapsedTime), rcReportGen.UNITS_FLOAT)
00978     self.__reportGen.addField("EndTime", str(time.gmtime(self.__endTime)))
00979     self.__reportGen.addField("FreeDiskSpace", str(FreeSpace.FreeSpace()), rcReportGen.UNITS_LONG)
00980     self.__reportGen.addField("AdditionalInputFiles", str(self.getAddlInputFiles()))
00981     failed = []
00982     failedNoVersion = []
00983     releases = {}
00984 
00985     if int(self.prefs["versionnbl"]) == 1:
00986       try:
00987         self.gui.waitCursor()
00988         versions = Gversions.getModuleVersions(self.common.getLCATcmd().LCM)
00989         for (module, version) in versions.items():
00990           if type(version) is types.StringType:
00991             if version.strip() == '':
00992               failedNoVersion.append(module)
00993               failed.append(module)
00994         self.gui.normalCursor()
00995       except:
00996         log.exception("Error in retrieving version information. Setting VersionData to an empty list.")
00997         versions = {}
00998       self.__reportGen.addField("VersionData", str(versions))
00999 
01000     if int(self.prefs["disableverification"]) == 0:
01001       try:
01002         filesToVerify= []
01003         for rootDir in ROOT_DIRS:
01004           if os.path.exists(os.path.join(ONLINE_ROOT, rootDir)):
01005             release = Gversions.verifyModuleDigests(digestDataFile=None, rootDir=rootDir,
01006                                                     additionalFilesToVerify=filesToVerify)
01007             releases[rootDir] = release[0]
01008             for module in release[1]:
01009               if module not in failed:
01010                 failed.append(module)
01011             noVersionInRelease = False
01012             for module in failedNoVersion:
01013               if module in release[1]:
01014                 noVersionInRelease = True
01015                 break
01016             if len(release[1]) > 0 or noVersionInRelease:
01017               log.error("Some modules failed verification for %s release %s. Check Elogbook for the modules that failed." % (rootDir, release[0]))
01018       except:
01019         log.exception("Error in verification. Setting Release and ModulesFailedVerification to empty lists.")
01020         releases = {}
01021         failed = []
01022     self.__reportGen.addField("Release", str(releases))
01023     self.__reportGen.addField("ModulesFailedVerification", str(failed))
01024 
01025     if self.prefs.has_key("logfilename"):
01026       self.__reportGen.addField("LogFile", self.prefs["logfilename"])
01027     if self.getRunMsgLogFile() is not None:
01028       self.__reportGen.addField("RunLogFile", self.getRunMsgLogFile())
01029     if self.getRunLcmLogFile() is not None:
01030       self.__reportGen.addField("LcmLogFile", self.getRunLcmLogFile())
01031     comments = []
01032     if self.rc is not None:
01033       cw = self.rc.getCommentWidget()
01034       if cw is not None:
01035         hist = cw.getHistory()
01036         tstamp = cw.getTimestamps()
01037         comments += zip(tstamp, hist)
01038       cw = self.rc.getEndRunComments()
01039       if cw is not None:
01040         hist = cw.getHistory()
01041         tstamp = cw.getTimestamps()
01042         comments += zip(tstamp, hist)
01043     self.__reportGen.addField("Comments", str(comments))
01044     for (key, value) in self.__customReportData.items():
01045       self.__reportGen.addField(key, str(value))
01046     if self.prefs is not None:
01047       prefs = self.prefs
01048       if prefs.has_key("lastsite"):
01049         self.__reportGen.addField("Site",           prefs["lastsite"])
01050       if prefs.has_key("lastparticletype"):
01051         self.__reportGen.addField("ParticleType",   prefs["lastparticletype"])
01052       if prefs.has_key("lastinstrumenttype"):
01053         self.__reportGen.addField("InstrumentType", prefs["lastinstrumenttype"])
01054       if prefs.has_key("lastorientation"):
01055         self.__reportGen.addField("Orientation",    prefs["lastorientation"])
01056       if prefs.has_key("lastphase"):
01057         self.__reportGen.addField("Phase",          prefs["lastphase"])
01058       if prefs.has_key("hwConfig"):
01059         self.__reportGen.addField("hwConfig", str(prefs["hwConfig"]))
01060     if self.__verifyStatus is not None:
01061       self.__reportGen.addField("ParamVerify", str(self.__verifyStatus))
01062     if self.__testReportFile is not None:
01063       self.__reportGen.addField("ScriptReport", self.__testReportFile)
01064 
01065     errorLogCount = self.common.getErrorLogCount()
01066     self.__reportGen.addField("ErrorLogCount", str(errorLogCount), rcReportGen.UNITS_INT)
01067     # If there are any error logs during the run override the status as FAILED
01068     if self.getCompletionStatus() == self.COMPL_STATUS_PASSED and errorLogCount != 0:
01069       log.warn("Errors were encountered during the run. Overriding the completion status as FAILED.")
01070       self.__appStatus = self.COMPL_STATUS_FAILED
01071 
01072     # Does not apply to LICOS.
01073     ## As per Eduardo's request override the PASSED completion status to be
01074     ## INCOMPLETE if any of his criteria (see __isTestComplete) aren't met
01075     #elif self.getCompletionStatus() == self.COMPL_STATUS_PASSED and \
01076     #       not self.__isTestComplete():
01077     #  log.warn("Test was not complete. Overriding the completion status as INCOMPLETE.")
01078     #  self.__appStatus = self.COMPL_STATUS_INCOMPLETE
01079 
01080     self.__reportGen.addField("CompletionStatus", str(self.getCompletionStatus()), rcReportGen.UNITS_INT)
01081     self.__reportGen.addField("CompletionStatusStr", self.getCompletionStatusStr())
01082 
01083     self.__reportGen.addField("ScriptOptions", str(self.common.getScriptOpts(self.getName())))
01084 
01085     # We're done!
01086     self.__reportGen.writeReportXML()
01087     try:
01088       if self.prefs is not None and int(self.prefs["elognbl"]):
01089         self.__endActivity()
01090     except Exception, e:
01091       log.exception(e)
01092       raise
01093 
01094     log.debug("Run report generation complete.")
01095 
01096   def startExcludeTime(self):
01097     """!\brief Save the start time for exclusion from the run's elapsed time.
01098 
01099     This method allows excluding some amount of time from the run's elapsed
01100     time.  The start time is the time that the method is called.
01101 
01102     Note that time spent in the PAUSED state is also excluded from the elapsed
01103     time value.  Overlapping time is excluded from the elapsed time only once.
01104     """
01105     if not self.__excluding:
01106       self.__excludeLock.acquire()
01107       now = time.time()
01108       self.__excluding = True
01109       self.__excludeTime  = now
01110       self.__elapsedTime += now - self.__strobeTime
01111       self.__strobeTime   = now
01112       self.__excludeLock.release()
01113     elif self.__pauseTime is not None:
01114       pass                              # Going into PAUSED started it
01115     else:
01116       assert not self.__excluding, "Error: " + \
01117              "Attempt made to exclude time when it is already being excluded"
01118 
01119   def endExcludeTime(self):
01120     """!\brief Strobe the end time for exclusion from the run's elapsed time.
01121 
01122     This method allows excluding some amount of time from the run's elapsed
01123     time.  The end time is the time that the method is called.  The difference
01124     between the end time and the start time is added to the excluded time.
01125     """
01126 
01127     if self.__pauseTime is None:
01128       assert self.__excluding, "Error: startExcludeTime was not called"
01129       self.__excludeLock.acquire()
01130       now = time.time()
01131       dExcludedTime         = now - self.__excludeTime
01132       self.__excludedTime += dExcludedTime
01133       self.__elapsedTime  += now - self.__strobeTime - dExcludedTime
01134       self.__strobeTime    = now
01135       self.__excludeTime   = None
01136       self.__excluding     = False
01137       self.__excludeLock.release()
01138     else:                               # In PAUSED state: nothing to do
01139       pass
01140 
01141   def adjustExcludeTime(self, dExcludeTime):
01142     """!\brief This method allows adjustment of the amount of time to be
01143     excluded from the run's elapsed time.
01144 
01145     The amount of time to be excluded from the run's elapsed time can be
01146     adjusted positivily or negativily with this method.  It does not take
01147     into account any other effects on the excluded time, such as whether
01148     the run was in the PAUSED state.
01149 
01150     \param dExcludeTime  The amount of time to be excluded as given by the
01151     difference of two time.time() values.
01152     """
01153     # Ensure this doesn't interrupt another thread diddling excludeTime
01154     self.__excludeLock.acquire()
01155     self.__excludedTime += dExcludeTime
01156     self.__excludeLock.require()
01157 
01158   def __jacket(self, function, *args, **kwargs):
01159     """!\brief Jacketting method for catching exceptions
01160 
01161     This method is used for catching exceptions and initiating graceful
01162     termination of the user application.
01163 
01164     \param function The function being jacketted
01165     \param *args    Unnamed arguments passed to \a function
01166     \param **kwargs Named arguments passed to \a function
01167     """
01168     try:
01169       function(*args, **kwargs)
01170     except Exception, e:
01171       log.exception("%s: %s" % (function.__name__, e))
01172       self.__appStatus = self.COMPL_STATUS_ABORTED
01173       self.gui.doReset()
01174 
01175   def __stopRun(self):
01176     """!\brief Logic executed during a stop run.
01177 
01178     This method is responsible for:
01179     - Calculating the final elapsed time
01180     - Bringing up the end run dialog
01181     - Setting the completion status
01182     - Creating the test report
01183     """
01184     # Calculate the final elapsed time
01185     now = time.time()
01186     self.__endTime      = now
01187     self.__excludeLock.acquire()
01188     if self.__strobeTime is not None:
01189       self.__elapsedTime += now - self.__strobeTime
01190     self.__strobeTime   = None
01191     self.__excludeLock.release()
01192 
01193     if self.__completionStatus == self.COMPL_STATUS_UNDEFINED:
01194       log.warn("Script completed with an undefined completion status. "
01195                "Please use the setCompletionStatus method.")
01196 
01197     # Let the operator override the completion status and add comments.
01198     if self.common.options().securedir is not None or \
01199        self.common.options().paramverify is not None or \
01200        self.common.options().endRunDlg is not None:
01201       if self.rc is not None and not self.rc.inShutdown:
01202         if not self.isRunFromSuite() or self.isLastSuiteScript():
01203           self.rc.execGUImethod(self.__handleEndRunDlg)
01204 
01205     # Create test report
01206     if self.rc is not None:
01207       if not self.rc.inShutdown:
01208         self.rc.execGUImethod(self.__genReport)
01209     else:
01210       self.__genReport()
01211 
01212   def __logEndRun(self, fn):
01213     """!\brief Log the end of the run
01214 
01215     \param fn The function name it is called from
01216     """
01217     log.info("%s -- Test %s finished, Run Id: %s, Status: %s, Completion Time: %s"
01218              % (fn, self.getName(), self.runId, self.getCompletionStatusStr(),
01219                 time.asctime(time.gmtime(self.__endTime))))
01220 
01221     if int(self.prefs["lognbl"]) == 1:
01222       self.common.stopRunLog(self.__runMsgLog)
01223       self.common.stopLcmLog()
01224       self.addExportedFile(self.__runMsgLogFile, delete=True)
01225       self.addExportedFile(self.__runLcmLogFile, delete=True)
01226 
01227   def __removeZeroLengthFile(self, filePath, fileName):
01228     """!\brief Delete the file if the file size is zero length.
01229 
01230     \param filePath
01231     \param fileName
01232 
01233     \return  0 If file size is not zero or if the deletion is successful.
01234             -1 If the deletion is not successful.
01235     """
01236     fname = os.path.join(filePath, fileName)
01237     size = os.stat(fname)[6]
01238     if size == 0:
01239       try:
01240         os.remove(fname)
01241       except:
01242         return -1
01243     return 0
01244 
01245   def __exportData(self):
01246     """!\brief Export all relevant data from a run.
01247 
01248       Routine to export all relevant data from a run to
01249       local storage and possibly to offsite backup.
01250       This routine is designed to execute after the completion of a run,
01251       but before teardown of Run Control.
01252       The name of the export directory is the run ID.  This is
01253       to guarantee against unintentional overwrites between multiple
01254       teststands.
01255     """
01256 
01257     # If we aren't exporting, return
01258     exportEnabled = (int(self.prefs["dataexport"]) == 1)
01259     if not exportEnabled: return
01260 
01261     # first thing is to create a local directory structure
01262     outDir = self.getRunId()
01263     outDirRoot = self.prefs["datadir"]
01264     outPath = os.path.join(outDirRoot,outDir)
01265 
01266     # Define two exporters.
01267     # One which purges the files after export, one which doesn't.
01268     hardExporter    = DataExport.DataExport()
01269     safeExporter    = DataExport.DataExport()
01270     hardExporter.setPurge(True)
01271     safeExporter.setPurge(False)
01272 
01273     if os.access(outDirRoot,os.W_OK):             # can we write?
01274       os.mkdir(outPath)
01275 
01276       # export housekeeping file
01277       # Change as per LTE-193
01278       #hskReadOnly = (int(self.prefs["envmonreadonly"]) == 1)
01279       #if self.__hsk is not None and (int(self.prefs["envmonnbl"]) == 1) and not hskReadOnly:
01280       #  self.addExportedFile(self.__hsk.getFileName(full=1), delete=True)
01281 
01282       # make the rcReport.out file
01283       rcLastLine = self.__rcLastLine()    # Grab the info from rcReport.out
01284       rcOutFile = open(os.path.join(outPath,"rcReport.out"), 'w')
01285       rcOutFile.write(rcLastLine)
01286       rcOutFile.close()
01287 
01288       for path,name,delete in self.__exportedFiles:
01289         try:
01290           if delete:
01291             hardExporter.export(os.path.join(path,name),os.path.join(outPath,name))
01292           else:
01293             safeExporter.export(os.path.join(path,name),os.path.join(outPath,name))
01294         except Exception, e:
01295           log.error("Exception %s in trying to export file %s" % (e, name))
01296 
01297     else: # Can't write to outDirRoot
01298       err = "rcTransitions.__exportData: Local directory ", outDirRoot, " is not writable"
01299       log.error(err)
01300       return         # Can't continue exporting, so return
01301 
01302 
01303     # if the data dir and the export dir are the same, don't export.
01304     if os.path.abspath(self.prefs["datadir"]) == os.path.abspath(self.prefs["exportdir"]):
01305       msg = "Data source and Export directories are identical\n" + \
01306             "source: %s, export: %s" % ( self.prefs["datadir"] , self.prefs["exportdir"] )
01307       log.warn(msg)
01308       exportEnabled = False
01309 
01310     # Export the directory to permanent storage
01311     if exportEnabled:
01312       if not os.path.exists(os.path.abspath(self.prefs["exportdir"])):
01313         log.fatal("Final export directory %s no longer exists.  Something has gone very, very wrong." % self.prefs["exportdir"])
01314         return
01315 
01316       destDir    = os.path.join(self.prefs["exportdir"] ,outDir)
01317       dbn = self.common.getDBN()
01318       dbn['_LastExportPath'] = destDir
01319       sourceDir  = outPath
01320       hardExporter.export(sourceDir, destDir)
01321 
01322       # done export.  Create an "I'm done" file in the export area
01323       done = file(destDir+'/zzz.done','w')
01324       done.close()
01325 
01326   def __rcLastLine(self):
01327     """!\brief Get the last line of the run report file.
01328 
01329     \return Last line of the run report.
01330     """
01331     # Now, we need the info from rcReport.out
01332     # As this routine runs after the writing of the file, just grab
01333     # the last usable line.
01334     filename = ( os.path.join(self.prefs["reportdir"], 'rcReport.out') )
01335 
01336     rcFile = open(filename,'r')
01337     charsPerLine = 4096                         # this probably needs tuning.
01338     while 1:
01339       try:                                      # read from the end of the file
01340         rcFile.seek(-1 * charsPerLine ,2)
01341       except IOError:                           # the file's shorter than charsPerLine
01342         rcFile.seek(0)                          # reposition at head
01343       if rcFile.tell() == 0:
01344         atstart=True
01345       else:
01346         atstart=False
01347 
01348       rcLines=rcFile.read().split("\n")         # last line of file is blank,
01349       if (len(rcLines) > 2) or atstart:         # so require 2 full lines
01350         break
01351                                                 # The lines are bigger than we thought,
01352       charsPerLine=charsPerLine * 2             # so double the read
01353 
01354     rcFile.close()
01355     rcLastLine = rcLines[len(rcLines)-2]        # cache this for later
01356 
01357     # extract the run id from rcLastLine, and compare it with what's expected
01358     import re
01359     runId = 0
01360     runIdRE = re.compile(r".*<RunId>(\d+)</RunId>.*")
01361     rList = runIdRE.findall(rcLastLine)
01362     if len(rList) > 0:
01363       runId = rList[0]
01364     else:
01365       log.fatal("Could not parse a run ID out of the last line of rcReport.out.")
01366       log.error("Last line = %s" % rcLastLine)
01367     if runId != self.getRunId():
01368       msg = "Run identifier read from rcReport.out (%s) does not match LICOS internal run ID (%s)" % (runId, self.getRunId())
01369       log.fatal(msg)
01370 
01371     return rcLastLine
01372 
01373 
01374   def __handleEndRunDlg(self):
01375     """!\brief Handle the end run dialog.
01376 
01377     """
01378     prefs = self.common.preferences()
01379     secMan = self.common.getSecurityMan()
01380     endRunDlg = self.rc.getEndRunDlg()
01381     loginId = prefs["operatorobj"].getLoginId()
01382     endRunDlg.initialize(secMan, loginId, self)
01383     if endRunDlg.exec_loop() == endRunDlg.Accepted:
01384       curComplStatusStr = self.getCompletionStatusStr()
01385       (cv, cs) = endRunDlg.getNewCompletionStatus(curComplStatusStr)
01386       if cv is not None:
01387         self.setCompletionStatus(cv)
01388         msg = ("Completion Status '%s' is overridden with status '%s' by operator %s" %
01389               (curComplStatusStr, cs, prefs["operator"]))
01390         log.info(msg)
01391         endRunDlg.enterComment(msg, addToHistory=1)
01392     else:
01393       endRunDlg.clearHistory()
01394 
01395   class StopRequestedException(Exception):
01396     def __init__(self, msg, *args):
01397       self.msg = msg
01398       self.args = (msg,) + args
01399 

Generated on Thu Apr 27 20:52:43 2006 for LICOS L02-01-00 by doxygen 1.4.6-NO