00001
00002
00003
00004
00005
00006
00007
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
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
00029 from LICOS.lib.cmdTlmDb.LCATtlmDb import LCATtlmDb
00030 from LICOS.lib.cmdTlmDb.LCATtlmDb import LICOS_TlmPacketFactory
00031
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
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
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098 raw = cv.rawValue()
00099
00100
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
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
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
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
00149 finalList += line + "\n"
00150
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
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
00174 self.__FSWPacketList = []
00175 self.__CMDPacketList = []
00176
00177
00178 self.obtainMnemonics()
00179
00180
00181
00182 self.__client = CvtPacketMonitorClient(cvtHost,cvtPort,self.__mnemList,tlmdb,self,self.__guiBridge)
00183
00184
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
00191
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
00208 self.connect(self.menuPreferencesAction, SIGNAL("activated()"), self.accessPrefs)
00209
00210
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
00269
00270
00271
00272
00273
00274
00275 def decomPacket(self, packet):
00276 """ Not used at the moment.
00277 """
00278
00279 pktStr = packet
00280 pktHdr = struct.unpack('!HHH', packet)
00281 apId = ( pktHdr[0] & 0x07FF )
00282
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
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
00354 if view.childCount() >= self.maxRow:
00355
00356 iterator = QListViewItemIterator(view.lastItem())
00357
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
00365
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
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
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
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
00477
00478
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
00548 options = gOptions.Options(['cvtHost', 'vscConfig'])
00549 try:
00550 options.parse()
00551 except Exception, msg:
00552 options.usage(str(msg))
00553
00554 sys.exit()
00555
00556 config = ConfigParser.ConfigParser()
00557 config.read(options.vscConfig)
00558
00559
00560 cvtHost = options.cvtHost
00561
00562 portTool = VscProxyPorts(config.getint('vsc', 'proxyPortBase'))
00563 cvtPort = portTool.cvtOut()
00564
00565
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
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
00580 w.show()
00581 QObject.connect(a,SIGNAL("lastWindowClosed()"),w.quit)
00582
00583
00584
00585
00586 threading.Thread(target=asyncore_loop).start()
00587 a.exec_loop()
00588
00589
00590 killEvent.set()
00591
00592