AlarmHandler.py

Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 #                               Copyright 2006
00004 #                                     by
00005 #                        The Board of Trustees of the
00006 #                     Leland Stanford Junior University.
00007 #                            All rights reserved.
00008 #
00009 #
00010 
00011 __facility__ = "Online"
00012 __abstract__ = "Classes for determining alarm states"
00013 __author__   = "J. Panetta <panetta@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online"
00014 __date__     = "12/29/2005" # Created
00015 __updated__  = ("$Date: 2006/04/28 01:20:18 $").split(' ')[1]
00016 __version__  = "$Revision: 1.6 $"
00017 __release__  = "$Name: HEAD $"
00018 __credits__  = "SLAC"
00019 
00020 import LICOS.copyright_SLAC
00021 
00022 import threading
00023 import sys, os, asyncore
00024 from ConfigParser              import ConfigParser
00025 
00026 from qt import QApplication, QMainWindow, QObject
00027 from qt import QPopupMenu, QString, Qt, QColor, QTimer
00028 from qt import SIGNAL, SLOT
00029 
00030 # JHP: must construct QApplication before QLV_AlarmedItem
00031 #     (paint object error in QT)
00032 mainApp = QApplication(sys.argv)
00033 killEvent = threading.Event()
00034 killEvent.clear()
00035 
00036 from LICOS.core.thread.guiBridges    import GUIbridge
00037 from LICOS.tools.alarm.AlarmWindow   import AlarmWindow
00038 from LICOS.tools.alarm.AlarmObjects  import QLV_AlarmedItem, AlarmClient
00039 from LICOS.tools.alarm.AlarmStates   import AlarmState
00040 from LICOS.tools.proxy.VscProxyPorts import VscProxyPorts
00041 from ISOC.TlmUtils.TlmRdbInterface   import TlmRdbDb
00042 
00043 
00044 lightGray = QColor(0xd0,0xd0,0xd0)
00045 UPDATE_CHECK_TIME = 20   # seconds
00046 
00047 
00048 #class Limit(object):
00049 #  def __init__(self ,name=None, desc=None, descLong=None ,version=None, redLo=None, yelLo=None, yelHi=None, redHi=None):
00050 #    self.name = name
00051 #    self.desc = desc
00052 #    self.descLong = descLong
00053 #    self.version = version
00054 #    self.redLo = redLo
00055 #    self.yelLo = yelLo
00056 #    self.yelHi = yelHi
00057 #    self.redHi = redHi
00058 
00059 
00060 def asyncore_loop(timeout=1.0, use_poll=False, map=None):
00061   """replace the asyncore central loop and run it its own thread.
00062      Valid for Python 2.3.x
00063      Python 2.4 has an extra agument for loop, count.
00064   """
00065   if map is None:
00066       map = asyncore.socket_map
00067 
00068   if use_poll:
00069     if hasattr(select, 'poll'):
00070       poll_fun = asyncore.poll3
00071     else:
00072       poll_fun = asyncore.poll2
00073   else:
00074     poll_fun = asyncore.poll
00075 
00076   while map and not killEvent.isSet():
00077     poll_fun(timeout, map)
00078 
00079 
00080 # needs for V 1:
00081 #  3) Alarm window
00082 #  4) Disabled alarm set.
00083 #  5) Record the alarms somewhere???
00084 
00085 # Wishes for V 2:
00086 #  1) Expand hierarchy where there are non-green/black items
00087 #  2) If adding mnems multiple times, wire them correctly
00088 
00089 
00090 
00091 class AlarmHandler(AlarmWindow):
00092   def __init__(self, config, vscConfig, cvtHost,
00093                popAlarmSwitch = True,
00094                parent=None, name = None, fl = 0,):
00095     AlarmWindow.__init__(self, parent, name, fl)
00096 
00097     self.MainAlarmListView.addColumn('Item')
00098     self.MainAlarmListView.addColumn('Raw Value')
00099     self.MainAlarmListView.addColumn('Eng Value')
00100     self.MainAlarmListView.addColumn('Description')
00101     self.MainAlarmListView.setSorting(0)
00102     self.MainAlarmListView.setPaletteBackgroundColor(lightGray)
00103 
00104     self.__guiBridge = GUIbridge(threading.currentThread())
00105 
00106     self.__configuration = config
00107     self.__vscConfig     = vscConfig
00108     self.__cvtHost       = cvtHost
00109 
00110     self.__alarmItems    = {}
00111     self.__source        = None
00112     self.__tlmDb         = None
00113     self.__cvc           = None
00114 
00115     self.__popAlarmSwitch = popAlarmSwitch
00116     self.__prevUpdateCounter = -1
00117 
00118     self.connect(self.MainAlarmListView,
00119                  SIGNAL("contextMenuRequested(QListViewItem *, const QPoint &, int)"),
00120                  self.contextMenuShow)
00121 
00122     self.buildContextMenu()
00123     self.__tlmDb, self.__source = self.__initDatabase()
00124 
00125 #   set Window caption
00126     try:
00127       caption = config.get("GUI","caption")
00128     except:
00129       caption = 'Alarm Window'
00130     self.setCaption(QString(caption))
00131 
00132     self.configure()
00133 
00134     #setup timer
00135     try:
00136       delay = int(config.get("timer","delay"))*1000
00137     except:
00138       delay = UPDATE_CHECK_TIME * 1000
00139     self.__timer = QTimer()
00140     self.__timer.connect(self.__timer, SIGNAL("timeout()"), self.__timerEvent)
00141     self.__timer.start(delay, False)
00142 
00143   def fileExit(self):
00144     self.close()
00145 
00146   def close(self, qParam = False):
00147     self.__guiBridge.shutdown()
00148     return QMainWindow.close(self, qParam)
00149 
00150   def configuration(self):
00151     return self.__configuration
00152 
00153   def configure(self):
00154 
00155     # build toplevel gui
00156     config = self.configuration()
00157     if not config.has_section('top_level'):
00158       msg = "Configuration lacks 'top_level' section.  Cannot configure AlarmHandler"
00159       raise RuntimeError(msg)
00160 
00161     # add the subItems to the top level
00162     self.__alarmItems['top_level'] = self.MainAlarmListView
00163     for tlItem in self.__getMembers('top_level'):
00164       self.addItem(tlItem, 'top_level')
00165 
00166     # Determine valid mnemonics from db
00167     validMnems = []
00168     for mnem in self.__alarmItems.keys():
00169       if mnem not in validMnems and self.__tlmDb.mnem(mnem) is not None:
00170         validMnems.append(mnem)
00171         self.__alarmItems[mnem].setDescription(self.__tlmDb.mnem(mnem).desc())
00172 
00173     # wire in CVT
00174     ports = VscProxyPorts(self.__vscConfig.getint('vsc', 'proxyPortBase'))
00175     self.__cvc = AlarmClient( host = self.__cvtHost,
00176                               port = ports.cvtOut(),
00177                               gui  = self,
00178                               mnemList = validMnems)
00179     
00180     self.optionsAlarm_PopupsAction.setOn(self.__popAlarmSwitch)
00181     self.__cvc.registerAll()
00182 
00183   def addItem(self, itemName, parentName):
00184     """!\brief Add an item to the alarm handler.  Recursive.
00185 
00186     \param Name of the item to be added
00187     """
00188     config = self.configuration()
00189 
00190     parent = self.__alarmItems[parentName]
00191     thisItem = QLV_AlarmedItem(self, parent, str(itemName), AlarmState.NoLimit)
00192     self.__alarmItems[itemName] = thisItem
00193 
00194     if itemName in config.sections():
00195       if config.has_option(itemName, 'expanded'):
00196         expanded = config.getboolean(itemName, 'expanded')
00197         thisItem.setOpen( expanded )
00198       if config.has_option(itemName, 'description'):
00199         thisItem.setDescription( config.get(itemName, 'description').strip() )
00200       thisItem.setRawValue()
00201 
00202       for member in self.__getMembers(itemName):
00203         self.addItem(member, itemName)
00204       thisItem.sortChildItems(0, True)
00205 
00206 
00207   def __getMembers(self, itemName):
00208     """!\brief retrieve a list of members of an alarmed hierarchy entry
00209 
00210     """
00211     config = self.configuration()
00212     if config.has_option(itemName, 'members'):
00213       mStr = config.get(itemName, 'members')
00214       mList = [ a.strip() for a in mStr.split(',') ]
00215     else:
00216       mList = []
00217 
00218     return mList
00219 
00220   def alarmItems(self):
00221     return self.__alarmItems
00222 
00223   def tlmDb(self):
00224     return self.__tlmDb
00225 
00226   def guiBridge(self):
00227     return self.__guiBridge
00228 
00229   def contextMenuShow(self, item, pos, col ):
00230     self.__contextMenu.exec_loop(pos)
00231 
00232   def buildContextMenu(self):
00233     self.__contextMenu = QPopupMenu(self)
00234     self.__contextMenu.insertItem("Show Data", self.showData)
00235 #    self.__contextMenu.insertItem("Expand", self.expandChildren)
00236 #    self.__contextMenu.insertItem("Collapse", self.collapseChildren)
00237     self.__contextMenu.insertItem("Show Limits",self.showLimits)
00238     self.__contextMenu.insertItem("Acknowledge Alarms", self.acknowledge)
00239     self.__contextMenu.insertItem("Disable", self.disableAlarm)
00240     self.__contextMenu.insertItem("Enable", self.enableAlarm)
00241 
00242   def showData(self):
00243     # Note: selItem is a QLV_AlarmedItem
00244     selItem = self.MainAlarmListView.selectedItem()
00245     if selItem is not None:
00246       selItem.showInfo()
00247 
00248   def showLimits(self):
00249     # Note: selItem is a QLV_AlarmedItem
00250     selItem = self.MainAlarmListView.selectedItem()
00251     if selItem is not None:
00252       selItem.showLimits()
00253 
00254   def expandChildren(self):
00255     # Note: selItem is a QLV_AlarmedItem
00256     selItem = self.MainAlarmListView.selectedItem()
00257     if selItem is not None:
00258       selItem.expandChildren(True)
00259 
00260   def collapseChildren(self):
00261     # Note: selItem is a QLV_AlarmedItem
00262     selItem = self.MainAlarmListView.selectedItem()
00263     if selItem is not None:
00264       selItem.expandChildren(False)
00265 
00266   def acknowledge(self):
00267     # Note: selItem is a QLV_AlarmedItem
00268     selItem = self.MainAlarmListView.selectedItem()
00269     if selItem is not None:
00270       selItem.recalculate()
00271 
00272   def disableAlarm(self):
00273     selItem = self.MainAlarmListView.selectedItem()
00274     selItem.disable()
00275 #   force update
00276     selItem.setState(selItem.state(),forceUpdate=True)
00277 
00278   def enableAlarm(self):
00279     selItem = self.MainAlarmListView.selectedItem()
00280     selItem.enable()
00281     #force update
00282     selItem.setState(selItem.state(),forceUpdate=True)
00283 
00284   def customEvent(self, e):
00285     self.__guiBridge.handleCustomEvent(e)
00286 
00287   def __cfgDb(self, config):
00288     import psycopg
00289     dsn = config.get('postgres', 'dsn')
00290     db  = psycopg.connect(dsn)
00291 
00292     return db
00293 
00294   def __initDatabase(self):
00295     config = self.__vscConfig
00296 
00297     csect = config.sections()
00298     sect = "tlmdb"
00299     if sect not in csect:
00300       msg = "Config file %s is missing required section: %s" % (config, sect)
00301       raise KeyError, msg
00302 
00303     source = int(config.get("tlmdb", "source"))
00304     build  = config.get("tlmdb", "build")
00305 
00306     db = self.__cfgDb(config)
00307     cur = db.cursor()
00308 
00309     tlmDb = TlmRdbDb(cur)
00310 
00311     tlmDb.populate(source=source, build=build)
00312 
00313     return (tlmDb, source)
00314 
00315   def __timerEvent(self):
00316     val = self.__cvc.getUpdateCount()
00317     if val == self.__prevUpdateCounter:
00318       self.leUpdateCounter.setPaletteBackgroundColor(Qt.red)
00319     else:
00320       self.leUpdateCounter.setPaletteBackgroundColor(lightGray)
00321     self.__prevUpdateCounter = val
00322     self.leUpdateCounter.setText(QString(str(val)))
00323     x = self.MainAlarmListView.sizeHint()
00324 
00325   def optionsAlarmPopups(self, state):
00326     if state:
00327       self.__popAlarmSwitch = True
00328     else:
00329       self.__popAlarmSwitch = False
00330 
00331   def popAlarms(self):
00332     # Return True or False depending on whether popup alarms are active
00333     return self.__popAlarmSwitch
00334 
00335 
00336 def main(options):
00337   # mainApp is defined in include region at start of file.
00338 
00339   config = ConfigParser()
00340   config.read(options.config)
00341 
00342   vscConfig = ConfigParser()
00343   vscConfig.read(options.vscConfig)
00344 
00345   cvtHost    = options.cvtHost
00346 
00347   #check for no Popup Alarm Box switch
00348   if options.noPopAlarm is None:
00349     popAlarmSwitch = True
00350   else:
00351     popAlarmSwitch = False
00352 
00353   p = AlarmHandler(config, vscConfig, cvtHost, popAlarmSwitch)
00354   mainApp.setMainWidget(p)
00355 
00356   p.show()
00357   QObject.connect(mainApp, SIGNAL("lastWindowClosed()"), mainApp, SLOT("quit()"))
00358   # start the asyncore loop.  Termination is set by killEvent
00359   threading.Thread(target=asyncore_loop).start()
00360 
00361   mainApp.exec_loop()
00362   killEvent.set()
00363 
00364 def usage():
00365   s = '/'
00366   if os.name == 'nt':  s = '\\'
00367   name = sys.argv[0].split('.')[-2].split(s)[-1]
00368   return "%s is used to display telemetry data from a database." % (name)
00369 
00370 
00371 
00372 if __name__ == '__main__':
00373 
00374   from LICOS.util.gOptions import Options
00375 
00376   options = Options(['config', 'vscConfig', 'cvtHost'],switches = ['noPopAlarm'])
00377   try:
00378    options.parse()
00379   except Exception, msg:
00380    options.usage(usage())
00381    raise Exception, msg
00382   x = options.noPopAlarm
00383 
00384   main(options)
00385 

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