packetMonitorImpl.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__ = "GLAST ISOC Packet Monitor"
00012 __author__   = "A. Kavelaars <aliciak@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online"
00013 __date__     = "19/10/2005"
00014 __version__  = "$Revision: 1.16 $"
00015 __credits__  = "SLAC"
00016 
00017 import LICOS.copyright_SLAC
00018 
00019 import sys, struct #, os
00020 import traceback
00021 import asyncore, psycopg
00022 import threading
00023 import time
00024 from   binascii                                       import hexlify
00025 from   qt                                             import *
00026 import ConfigParser
00027 import LICOS.util.gOptions                            as     gOptions
00028 #~ from   LCAT_tlm_db                                    import TlmApidDict
00029 from   LICOS.lib.cmdTlmDb.LCATtlmDb                   import LCATtlmDb 
00030 from   LICOS.lib.cmdTlmDb.LCATtlmDb                   import LICOS_TlmPacketFactory
00031 #~ from   LICOS.lib.cmdTlmDb.LCATtlmDb                   import LICOS_TlmPacketFactory
00032 from   ISOC.ProductUtils                              import ProductSpan
00033 from   ISOC.TlmUtils.SpecialDecom                     import *
00034 from   ISOC.TlmUtils.TlmRdbInterface                  import TlmRdbDb
00035 from   LICOS.lib.currValTable.CurrValClient           import CurrValClient
00036 from   LICOS.core.thread.guiBridges                   import GUIbridge
00037 from   LICOS.tools.monitor.packetMonitor              import packetMonitor
00038 from   LICOS.tools.monitor.packetMonitorPrinter       import packetMonitorPrinter
00039 from   LICOS.tools.monitor.packetMonitorPrefsImpl     import packetMonitorPrefsImpl
00040 from   LICOS.tools.proxy.VscProxyPorts                import VscProxyPorts
00041 
00042 # Define kill event.
00043 killEvent = threading.Event()
00044 killEvent.clear()
00045 
00046 def asyncore_loop(timeout=1.0, use_poll=False, map=None):
00047   """replace the asyncore central loop and run it in its own thread.
00048      Valid for Python 2.3.x
00049      Python 2.4 has an extra agument for loop, count.
00050   """
00051   if map is None:
00052       map = asyncore.socket_map
00053 
00054   if use_poll:
00055     if hasattr(select, 'poll'):
00056       poll_fun = asyncore.poll3
00057     else:
00058       poll_fun = asyncore.poll2
00059   else:
00060     poll_fun = asyncore.poll
00061 
00062   while map and not killEvent.isSet():
00063     poll_fun(timeout, map)
00064 
00065 class CvtPacketMonitorClient(CurrValClient):
00066   """ Class that connects to CVT as a client and passes packet 
00067       information to the PacketMonitor GUI. It uses SpecialDecom
00068       to decom the packet payload information.
00069       
00070       \param host       CVT host.
00071       \param port       CVT port.
00072       \param mnemList   List of apid mnemonics to look into.
00073       \param tlmdb      Postgress Telemetry database CVT is connected to.
00074       \param guiBridge  Qt Command Handler.
00075   """
00076       
00077   def __init__(self, host, port, mnemList, tlmdb, gui, guiBridge):
00078     CurrValClient.__init__(self, host, port, mnemList=mnemList)
00079     self.__items = {}
00080     self.__gui = gui
00081     self.__guiBridge = guiBridge
00082     self.__tlmdb = tlmdb
00083  
00084   def update(self, cv):
00085     self.__guiBridge.execGUImethodNR(self.__gui, self.__update, cv)
00086 
00087   def __update(self, cv):
00088     # Process all mnemonics from CVT. May be needed in the future
00089     #~ if cv.name not in self.__items:
00090       #~ self.__items[cv.name] = QListViewItem(self.__gui.telemView)
00091       #~ self.__items[cv.name].setText(0, cv.name)
00092     #self.__items[cv.name].setText( 1, str(len(raw)) )
00093     #~ egu = raw
00094     #~ mObj = self.__tlmdb.mnem(cv.name)
00095     #~ if mObj is not None:
00096       #~ egu = mObj.conversion().egu(raw)
00097       #~ self.__items[cv.name].setText( 2, str(egu) )
00098     raw = cv.rawValue()
00099 
00100     # Pick up apids 720 and 725 and process them with SpecialDecom
00101     appIdPkt = LICOS_TlmPacketFactory(raw)
00102     ts = appIdPkt.secs  
00103     tu = appIdPkt.usecs/1E6
00104     appTime = ProductSpan.utcfromtimestamp( ts, tu )
00105     appSeq = appIdPkt.seqNum
00106     appLen = appIdPkt.length
00107     try:
00108       pktHdr = struct.unpack('!HHH', raw[:6])
00109       appId = ( pktHdr[0] & 0x07FF )
00110     except:
00111       appId = 0
00112     #~ print appId
00113     if appId in (720, 725):
00114       if appId == 720:
00115         pktCli = CmdRespDecom(self.__gui.callBackCMDMon)
00116       elif appId == 725:
00117         pktCli = FswMsgDecom(self.__gui.callBackFSWMon)
00118       pktCli.decom(appId, raw)
00119     
00120     # Pick up ALL apids and show them for the CCSDS Monitor
00121     if not self.__gui.pauseCCSDSButton.isOn():
00122       if self.__gui.filterByAPIDButton.isOn():
00123         if str(appId) != str(self.__gui.apid.text()):
00124           return
00125       raw = hexlify(raw)
00126       rawList = list(raw)
00127       finalPos = len(rawList)
00128       pos = 0
00129       binList = []
00130       while pos < finalPos:
00131         item = rawList[pos] + rawList[pos+1]
00132         binList.append(item)
00133         pos += 2
00134       #~ print binList
00135       finalPos = len(binList)
00136       offset = 0
00137       pos = 0
00138       finalList = ""
00139       while offset < finalPos:
00140         item = binList[offset]
00141         line = "%08X" % (offset) + " "
00142         for i in range(0, 32):
00143           try:
00144             line += " " + binList[offset]
00145             offset += 1
00146           except:
00147             pass
00148         #~ print line
00149         finalList += line + "\n" 
00150       #~ print finalList
00151       self.__gui.CCSDSView.setText(str(finalList))  
00152       self.__gui.apid.setText(str(appId))
00153       self.__gui.time.setText(str(appTime))
00154       self.__gui.seqN.setText(str(appSeq))
00155       self.__gui.size.setText(str(appLen))
00156 
00157 
00158 class packetMonitorImpl(packetMonitor):
00159   def __init__(self,cvtHost,cvtPort,tlmdb,parent = None,name = None,fl = 0):
00160     packetMonitor.__init__(self,parent,name,fl)
00161     
00162     # Define Configuration Parser and Preferences Dialog
00163     self.__confParser = ConfigParser.ConfigParser()
00164     self.__prefsGUI = packetMonitorPrefsImpl(self.__confParser, self)
00165     self.passRowValues()
00166     
00167     self.__guiBridge = GUIbridge(threading.currentThread())
00168     self.__items = {}
00169     
00170     self.CMDView.setSorting(-1)
00171     self.FSWView.setSorting(-1)
00172   
00173     # Define Packet Lists to be filled when Pause buttons are pressed
00174     self.__FSWPacketList = []
00175     self.__CMDPacketList = []
00176 
00177     # Load mnemonics list.
00178     self.obtainMnemonics()
00179     #~ self.__mnemList = ['CmdConfirm', 'LCMMSGOUTC']    
00180   
00181     # Launch CVT client
00182     self.__client = CvtPacketMonitorClient(cvtHost,cvtPort,self.__mnemList,tlmdb,self,self.__guiBridge)
00183     
00184     # Initialize Context Menu and Clipboard
00185     self.contextMenu = QPopupMenu(self)
00186     self.contextMenu.insertItem("Copy Ce&ll", self.copyToClipboard,    Qt.CTRL+Qt.Key_L)
00187     self.contextMenu.insertItem("Copy &Row",  self.copyRowToClipboard, Qt.CTRL+Qt.Key_R)
00188     self.clip = QApplication.clipboard()  
00189     
00190     #self.connect(self.refreshTelemButton, SIGNAL("clicked()"), self.plot)
00191     #self.connect(self.refreshCommButton,  SIGNAL("clicked()"), self.plot)
00192     self.connect(self.FSWView,SIGNAL("currentChanged(QListViewItem*)"),self.showFSWEntries)
00193     self.connect(self.CMDView,SIGNAL("currentChanged(QListViewItem*)"),self.showCMDEntries)
00194     
00195     self.connect(self.refreshFSWButton,   SIGNAL("clicked()"),       self.refreshFSWMon)
00196     self.connect(self.refreshCMDButton,   SIGNAL("clicked()"),       self.refreshCMDMon)
00197     self.connect(self.pauseFSWButton,     SIGNAL("clicked()"),       self.pauseFSWMon)
00198     self.connect(self.pauseCMDButton,     SIGNAL("clicked()"),       self.pauseCMDMon)
00199     self.connect(self.pauseCCSDSButton,   SIGNAL("clicked()"),       self.pauseCCSDSMon)
00200     self.connect(self.apid,               SIGNAL("returnPressed()"), self.filterByAPID)
00201     self.connect(self.apid,               SIGNAL("lostFocus()"),     self.filterByAPID)
00202     self.connect(self.filterByAPIDButton, SIGNAL("clicked()"),       self.filterByAPID)
00203     self.connect(self.printFSWButton,     SIGNAL("clicked()"),       self.printFSWMon)
00204     self.connect(self.printCMDButton,     SIGNAL("clicked()"),       self.printCMDMon)
00205     self.connect(self.commitButton,       SIGNAL("clicked()"),       self.closeGUI)
00206     
00207     # Access Preferences
00208     self.connect(self.menuPreferencesAction, SIGNAL("activated()"), self.accessPrefs)
00209 
00210     # Initialize Context Menu
00211     self.connect(self.FSWView,
00212                  SIGNAL("contextMenuRequested(QListViewItem*,const QPoint&,int)"),
00213                  self.FSWContextMenuShow)
00214     self.connect(self.CMDView,
00215                  SIGNAL("contextMenuRequested(QListViewItem*,const QPoint&,int)"),
00216                  self.CMDContextMenuShow)
00217 
00218   def FSWContextMenuShow(self, item, point, col):
00219     """ Show context menu for FSW Message Monitor.
00220         \param item   Current item.
00221         \param point  Current cursor position.
00222         \param col    Current col position.
00223     """
00224     self.contextMenuShow(self.telemView, item, point, col)
00225     
00226   def CMDContextMenuShow(self, item, point, col):
00227     """ Show context menu for CMD Message Monitor.
00228         \param item   Current item.
00229         \param point  Current cursor position.
00230         \param col    Current col position.
00231     """
00232     self.contextMenuShow(self.commView, item, point, col)
00233 
00234   def contextMenuShow(self, view, item, point, col):
00235     """ Pass current information to context menu and 
00236         execute it.
00237         \param view   Current view: FSW Message or CMD Response
00238                       Monitor.
00239         \param item   Current item.
00240         \param point  Current cursor position.
00241         \param col    Current col position.
00242     """
00243     if item is not None:
00244       self.__contextCell = item.text(col)
00245       text = ""
00246       numCols = view.columns()
00247       for j in range(0,numCols):
00248         text += str(item.text(j)) + " "
00249       self.__contextRow = text
00250       self.contextMenu.exec_loop(point, 0)
00251 
00252   def copyToClipboard(self):
00253     """ Pass selected item text to clipboard.
00254     """
00255     self.clip.setText(self.__contextCell)
00256 
00257   def copyRowToClipboard(self):
00258     """ Pass selected row text to clipboard.
00259     """
00260     self.clip.setText(self.__contextRow)
00261 
00262   def obtainMnemonics(self):
00263     """ Obtain Mnemonic appid dictionary.
00264     """
00265     LCAT = LCATtlmDb()
00266     self.__mnemList = LCAT.getNames()
00267     
00268     # Obsolete, obtain mnemonics from LICOS lib instead
00269     #~ apidDict = TlmApidDict()
00270     #~ self.__mnemList = []
00271     #~ for apid in apidDict.apids:
00272       #~ mnem = apidDict.apids[apid]
00273       #~ self.__mnemList.append(mnem)
00274   
00275   def decomPacket(self, packet):
00276     """ Not used at the moment.
00277     """
00278     # assuming packet is a packet from the CVT    
00279     pktStr = packet
00280     pktHdr = struct.unpack('!HHH', packet)
00281     apId = ( pktHdr[0] & 0x07FF )    
00282     # Decon packet using Bryson's 
00283     self.__sd.decom( apid, pktStr)
00284   
00285   def callBackFSWMon( self, dataDict ):
00286     """ Handle decom callback for FSW Response Packet.
00287     """
00288     if self.pauseFSWButton.isOn():
00289       self.appendFSWPacket(dataDict)
00290     else:
00291       self.displayFSWPacket(dataDict)
00292   
00293   def appendFSWPacket(self, dataDict):
00294     """ If pause FSW Message Monitor Button is on,
00295         append packet data to list.
00296     """    
00297     self.__FSWPacketList.append(dataDict)
00298   
00299   def displayFSWPacket(self, dataDict):
00300     """ If pause FSW Message Monitor Button is off, 
00301         display packet data in monitor.
00302     """
00303     listItem = QListViewItem(self.FSWView)  
00304     listItem.setText(0, "%s"           % dataDict['time'])
00305     listItem.setText(1, "%-4s"         % dataDict['node'])
00306     listItem.setText(2, "%-4s"         % dataDict['facility'])
00307     listItem.setText(3, "%-8s"         % dataDict['task'])
00308     listItem.setText(4, "%-32s"        % dataDict['function'])
00309     listItem.setText(5, "0x%08X"       % dataDict['code'])
00310     listItem.setText(6, "%-8s"         % dataDict['name'])
00311     listItem.setText(7, "%s"           % dataDict['text'])
00312     listItem.setText(8, "0x%04X"       % dataDict['trace'])
00313     listItem.setText(9, "0x%04X"       % dataDict['isr'])
00314     self.preventSaturation(self.FSWView, self.pauseFSWButton)
00315 
00316   def callBackCMDMon( self, dataDict ):
00317     """ Handle decom callback for CMD Response Packet.
00318     """
00319     if self.pauseCMDButton.isOn():
00320       #~ print "pause button is on"
00321       self.appendCMDPacket(dataDict)
00322     else:
00323       self.displayCMDPacket(dataDict)
00324   
00325   def appendCMDPacket(self, dataDict):
00326     """ If pause CMD Response Monitor Button is on, 
00327         append packet data to list.
00328     """
00329     self.__CMDPacketList.append(dataDict)
00330   
00331   def displayCMDPacket(self, dataDict):
00332     """ If pause CMD Response Monitor Button is off, 
00333         display packet data in monitor.
00334     """
00335     listItem = QListViewItem(self.CMDView)  
00336     listItem.setText(0, "%s"           % dataDict['exetime'])
00337     listItem.setText(1, "%s"           % dataDict['node'])
00338     listItem.setText(2, "%s"           % dataDict['task'])
00339     listItem.setText(3, "0x%03X(%04d)" % (dataDict['apid'],  dataDict['apid']))
00340     listItem.setText(4, "0x%04X(%05d)" % (dataDict['fcode'], dataDict['fcode']))
00341     listItem.setText(5, "0x%1X"        % dataDict['status'])
00342     listItem.setText(6, "%s"           % dataDict['payload'])
00343     self.preventSaturation(self.CMDView, self.pauseCMDButton)
00344 
00345   def preventSaturation(self, view, pauseButton):
00346     """ This function prevents the saturation of the FSWMsg and CMDResp list views.
00347         When the list view reaches MAX_ENTRIES, it will delete DELETE_ENTRIES list
00348         view items starting at the bottom. Since this is done at the bottom of the 
00349         list, the thread needs not to be paused.
00350         
00351         \param view    List View: FSWMsg view or CMDResp view.
00352     """
00353     #~ lastItem = view.lastChild()
00354     if view.childCount() >= self.maxRow:
00355       #~ pauseButton.setOn(1)
00356       iterator = QListViewItemIterator(view.lastItem())
00357       #~ print lastItem.text(0)
00358       iterator -= (self.delRow)
00359       item = iterator.current()
00360       while item is not None:
00361         saveItem = item.nextSibling()
00362         view.takeItem(item)
00363         item = saveItem
00364       #~ self.displayPacketList(view)
00365       #~ self.pauseButton.setOn(0)
00366   
00367   def passRowValues(self):
00368     """ Obtains Montior's maximum number of rows and number of rows deleted
00369         when this limit is reached from the user's preferences config file.
00370     """
00371     self.maxRow = int(str(self.__prefsGUI.maxRow.text()))
00372     self.delRow = int(str(self.__prefsGUI.delRow.text()))
00373     #~ print self.maxRow, self.delRow
00374 
00375   def customEvent(self, e):
00376     """This method overrides the QObject base class's.  There is generally
00377     no reason to call this method directly.  It is called by the Qt
00378     infrastructure whenever the custom event is posted, e.g. by the following
00379     three methods of this class: createGUI, execGUImethodNR and execGUImethod.
00380     """
00381     self.__guiBridge.handleCustomEvent(e)
00382     
00383   def quit(self):
00384     """ This method shuts down the gui bridge so that no update event is called
00385         once the QApp has quit. It is called in the __main__ portion of the 
00386         script.
00387     """
00388     self.__guiBridge.shutdown()
00389   
00390   def showFSWEntries(self):
00391     """ Displays in the status bar current number of packets 
00392         displayed in the FSW Message Monitor.
00393     """
00394     self.showEntries(self.FSWView, "FSW Message Monitor")
00395 
00396   def showCMDEntries(self):
00397     """ Displays in the status bar current number of packets 
00398         displayed in the CMD Message Monitor.
00399     """
00400     self.showEntries(self.CMDView, "CMD Response Monitor")
00401   
00402   def showEntries(self, view, viewName):
00403     """ Displays in the status bar current number of packets 
00404         displayed in the selected monitor.
00405         
00406         \param view           Selected Monitor: FSW Message or CMD Response.
00407         \param viewName       Monitor caption.
00408     """
00409     self.statusBar().message('Number of entries in ' + viewName + \
00410                              ': ' + str(view.childCount()), 2000)
00411   
00412   def accessPrefs(self):
00413     """ Excutes Preferences Dialog.
00414     """
00415     self.__prefsGUI.exec_loop()
00416   
00417   def fileOpen(self):
00418     """ Loads data from a file. TBD
00419     """
00420     self.statusBar().message('File Open not Implemented Yet.') 
00421     #self.obtainMnemonics()
00422     #~ path = os.path.join(os.environ['TOOLS_ROOT'],'Online/LICOS/lib/packetMonitor/Data')
00423     #~ self.__fn = str(QFileDialog.getOpenFileName(path, "(*.*)", self))
00424 
00425     #try:
00426     #~ pktCli = CmdRespDecom(self.callBackCmdList)
00427     #~ pktf = PktFile(self.__fn, pktCli, 1)
00428     # Obtain packets from file here, something that feeds into
00429     #~ pktCli.decom(apId, raw) 
00430     #~ instead of
00431     #~ pktf.dumpPkts()
00432     # Something like:
00433     # Plot data This will work for TelCom data instead of CVT way.
00434     #for packet in packetList:
00435       #self.decomPacket( packet )
00436   
00437   def refreshFSWMon(self):
00438     """ Clears rows in FSW Message Monitor.
00439     """
00440     self.FSWView.clear()
00441 
00442   def refreshCMDMon(self):
00443     """ Clears rows in CMD Response Monitor.
00444     """
00445     self.CMDView.clear()
00446   
00447   def pauseFSWMon(self):
00448     """Handles stored FSW Message Packets upon FSW Pause button click event.
00449     """
00450     pauseButton = self.pauseFSWButton
00451     if not pauseButton.isOn():
00452       for packet in self.__FSWPacketList:
00453         self.displayFSWPacket(packet)
00454       self.__FSWPacketList = []
00455       pauseButton.setText("P&ause")
00456     else:
00457       pauseButton.setText("&Resume")
00458   
00459   def pauseCMDMon(self):
00460     """Handles stored CMD Response Packets upon CMD Pause button click event.
00461     """
00462     pauseButton = self.pauseCMDButton
00463     if not pauseButton.isOn():
00464       for packet in self.__CMDPacketList:
00465         self.displayCMDPacket(packet)
00466       self.__CMDPacketList = []
00467       pauseButton.setText("Pa&use")
00468     else:
00469       pauseButton.setText("R&esume")
00470   
00471   def pauseCCSDSMon(self):
00472     """Handles stored FSW Message Packets upon FSW Pause button click event.
00473     """
00474     pauseButton = self.pauseCCSDSButton
00475     if not pauseButton.isOn():
00476       #~ for packet in self.__FSWPacketList:
00477         #~ self.displayFSWPacket(packet)
00478       #~ self.__FSWPacketList = []
00479       pauseButton.setText("Paus&e")
00480     else:
00481       pauseButton.setText("Resu&me")
00482   
00483   def filterByAPID(self):
00484     if self.filterByAPIDButton.isOn():
00485       self.apid.setReadOnly(0)
00486       self.time.clear()
00487       self.seqN.clear()
00488       self.size.clear()
00489       self.CCSDSView.clear()
00490     else:
00491       self.apid.setReadOnly(1)
00492 
00493   def filePrint(self):
00494     """ Prints to printer all monitors.
00495     """
00496     self.printFSWMon()
00497     self.printCMDMon()
00498   
00499   def printFSWMon(self):
00500     """ Pauses and Prints to printer FSW Message Monitor.
00501     """
00502     self.pauseFSWButton.setOn(1)
00503     self.printMon(self.FSWView, "FSW Message Monitor")
00504     self.pauseFSWButton.setOn(0)
00505   
00506   def printCMDMon(self):
00507     """ Pauses and Prints to printer CMD Response Monitor.
00508     """
00509     self.pauseCMDButton.setOn(1)
00510     self.printMon(self.CMDView, "CMD Response Monitor")
00511     self.pauseCMDButton.setOn(0)
00512   
00513   def printMon(self, view, viewName):
00514     """ Prints to printer selected Monitor.
00515     """
00516     printer = packetMonitorPrinter(parent = view)
00517     printer.secondHeader = None
00518     printer.printMon(viewName)
00519 
00520   def closeGUI(self):
00521     """ Closes GUI from Done Button.
00522     """
00523     self.close()
00524   
00525   def fileExit(self):
00526     """ Closes GUI from File Exit action.
00527     """
00528     self.close()
00529   
00530   def closeEvent(self, e):
00531     """ Manages close events, saving current window size.
00532     """
00533     self.__prefsGUI.saveMainWindowSize()
00534     e.accept()
00535 
00536   def keyPressEvent(self, ke):
00537     """ Manages special key press events.
00538     """
00539     if ke.key() == Qt.Key_Escape:
00540       self.close()
00541     elif ke.key() == Qt.Key_F5:
00542       self.refreshFSWMon()
00543       self.refreshCMDMon()
00544 
00545 
00546 if __name__ == "__main__":
00547   # Obtain options
00548   options = gOptions.Options(['cvtHost', 'vscConfig'])
00549   try:
00550     options.parse()
00551   except Exception, msg:
00552     options.usage(str(msg))
00553     #usage()
00554     sys.exit()
00555 
00556   config = ConfigParser.ConfigParser()
00557   config.read(options.vscConfig)
00558 
00559   # CVT connection
00560   cvtHost = options.cvtHost
00561   
00562   portTool = VscProxyPorts(config.getint('vsc', 'proxyPortBase'))
00563   cvtPort  = portTool.cvtOut()
00564 
00565   # TlmDB connection
00566   DSN = config.get('postgres', 'dsn')
00567   db = psycopg.connect( DSN )
00568   dbc = db.cursor()
00569   tlmdb = TlmRdbDb(dbc)
00570   tlmdb.populate( source = int(config.get("tlmdb", "source")),
00571                   build  = config.get("tlmdb", "build") )
00572   
00573   
00574   # Launch GUI
00575   a = QApplication(sys.argv)
00576   QObject.connect(a,SIGNAL("lastWindowClosed()"),a,SLOT("quit()"))
00577   w = packetMonitorImpl(cvtHost, cvtPort, tlmdb)
00578   a.setMainWidget(w)
00579   # Make sure no update is processed once the QApp is closed.
00580   w.show()
00581   QObject.connect(a,SIGNAL("lastWindowClosed()"),w.quit)
00582   
00583   # Start the asyncore loop.  Termination is set by killEvent
00584   # Must be started after the QApp is defined, before its
00585   # loop is executed.
00586   threading.Thread(target=asyncore_loop).start()
00587   a.exec_loop() 
00588   
00589   # Kill thread once QApp is closed.
00590   killEvent.set()
00591   
00592 

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