Change-Id: If95cb839ad81ca2a842ff7f6926847ac3928d8f2 Former-commit-id: 77e1a4d8f5237e5fae930c1e00589c752f8b3738
529 lines
19 KiB
Python
529 lines
19 KiB
Python
##
|
|
# This software was developed and / or modified by Raytheon Company,
|
|
# pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
|
#
|
|
# U.S. EXPORT CONTROLLED TECHNICAL DATA
|
|
# This software product contains export-restricted data whose
|
|
# export/transfer/disclosure is restricted by U.S. law. Dissemination
|
|
# to non-U.S. persons whether in the United States or abroad requires
|
|
# an export license or other authorization.
|
|
#
|
|
# Contractor Name: Raytheon Company
|
|
# Contractor Address: 6825 Pine Street, Suite 340
|
|
# Mail Stop B8
|
|
# Omaha, NE 68106
|
|
# 402.291.0100
|
|
#
|
|
# See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
|
# further licensing information.
|
|
##
|
|
#
|
|
# Name:
|
|
# MosViewer.py
|
|
# GFS1-NHD:A6642.0000-SCRIPT;1.38
|
|
#
|
|
# Status:
|
|
# DELIVERED
|
|
#
|
|
# History:
|
|
# Revision 1.38 (DELIVERED)
|
|
# Created: 17-JUL-2009 16:40:06 OBERFIEL
|
|
# Viewers now use a resource to determine the Routine button
|
|
# setting.
|
|
#
|
|
# Revision 1.37 (DELIVERED)
|
|
# Created: 26-MAR-2009 20:31:31 OBERFIEL
|
|
# Removed references to NGM MOS and made few other cosmetic
|
|
# changes
|
|
#
|
|
# Revision 1.36 (REVIEW)
|
|
# Created: 20-MAR-2009 18:22:15 OBERFIEL
|
|
# Removed code cruft. ETA changed to NAM. NGMMOS removed.
|
|
#
|
|
# Revision 1.35 (INITIALIZE)
|
|
# Created: 18-MAR-2009 10:51:14 GILMOREDM
|
|
# Now uses tagBalloon function to avoid certain issues with
|
|
# using the Balloon function
|
|
#
|
|
# Revision 1.34 (DELIVERED)
|
|
# Created: 30-DEC-2008 12:46:08 OBERFIEL
|
|
# Check to see that balloon remains below mouse pointer
|
|
#
|
|
# Revision 1.33 (DELIVERED)
|
|
# Created: 01-AUG-2008 15:44:44 OBERFIEL
|
|
# Synch'd up with changes in OB8.3.1
|
|
#
|
|
# Revision 1.32 (DELIVERED)
|
|
# Created: 19-MAR-2008 14:30:35 OBERFIEL
|
|
# Removed carriage returns.
|
|
#
|
|
# Revision 1.31 (DELIVERED)
|
|
# Created: 19-FEB-2008 14:52:33 GILMOREDM
|
|
# Fixed to allow users to enter TAF Editor when TAF
|
|
# generation using MOS data not possible.
|
|
#
|
|
# Revision 1.30 (REVIEW)
|
|
# Created: 25-JAN-2008 10:50:24 GILMOREDM
|
|
# Removed call to Pmw.Balloon method, because raised balloons
|
|
# created with that method were persisting when a "Check now"
|
|
# was triggered from the status window. Call to Balloon()
|
|
# method corrects this problem.
|
|
#
|
|
# Revision 1.29 (DELIVERED)
|
|
# Created: 14-MAY-2007 10:04:49 OBERFIEL
|
|
# Removed references to the obsolete prototype XTF product.
|
|
# Allow decoder and encoder to format TAF in two different
|
|
# ways. New format will be triggered by day and time to be
|
|
# specified at a later date.
|
|
#
|
|
# Revision 1.28 (DELIVERED)
|
|
# Created: 05-JAN-2007 10:54:14 OBERFIEL
|
|
# Update to put more balloon messages when MOS and LAMP
|
|
# messages are displayed in viewer.
|
|
#
|
|
# Revision 1.27 (DELIVERED)
|
|
# Created: 23-OCT-2006 11:05:32 BLI
|
|
# Fixed an outdated method name
|
|
#
|
|
# Revision 1.26 (DELIVERED)
|
|
# Created: 26-SEP-2006 09:37:05 BLI
|
|
# Modified to work with new TafGen
|
|
#
|
|
# Revision 1.25 (DELIVERED)
|
|
# Created: 02-AUG-2006 15:40:21 BLI
|
|
# Fixed showAll to use LampTafGenerator when gfs lamp is the
|
|
# data source
|
|
#
|
|
# Revision 1.24 (DELIVERED)
|
|
# Created: 06-FEB-2006 09:53:25 TROJAN
|
|
# missing check for undefined alternate sites
|
|
#
|
|
# Revision 1.23 (REVIEW)
|
|
# Created: 02-FEB-2006 15:58:02 TROJAN
|
|
# wrong method prevented display when "All" toggle set
|
|
#
|
|
# Revision 1.22 (APPROVED)
|
|
# Created: 02-FEB-2006 12:32:49 BLI
|
|
# Call LampTafGen when model=gfslamp
|
|
#
|
|
# Revision 1.21 (REVIEW)
|
|
# Created: 01-FEB-2006 15:15:38 TROJAN
|
|
# Incorrect handling of missing data
|
|
#
|
|
# Revision 1.20 (REVIEW)
|
|
# Created: 31-JAN-2006 08:35:26 TROJAN
|
|
# Change in naming convention for MOS/LAMP data, added LAMP
|
|
# to plotting module
|
|
#
|
|
# Revision 1.19 (INITIALIZE)
|
|
# Created: 30-JAN-2006 09:15:41 TROJAN
|
|
# stdr 958
|
|
#
|
|
# Revision 1.18 (APPROVED)
|
|
# Created: 27-JAN-2006 14:54:02 BLI
|
|
# Modified to toggle on/off prob. of LAMP
|
|
#
|
|
# Revision 1.17 (REVIEW)
|
|
# Created: 03-NOV-2005 13:39:38 TROJAN
|
|
# spr 7052
|
|
#
|
|
# Revision 1.16 (DELIVERED)
|
|
# Created: 15-SEP-2005 18:31:15 TROJAN
|
|
# spr 7023
|
|
#
|
|
# Revision 1.15 (DELIVERED)
|
|
# Created: 06-JUL-2005 21:01:41 TROJAN
|
|
# spr 6909
|
|
#
|
|
# Revision 1.14 (DELIVERED)
|
|
# Created: 07-MAY-2005 11:36:42 OBERFIEL
|
|
# Added Item Header Block
|
|
#
|
|
# Revision 1.13 (DELIVERED)
|
|
# Created: 18-APR-2005 18:56:32 BLI
|
|
# Added handler for hide prob. of cloud cover
|
|
#
|
|
# Revision 1.12 (APPROVED)
|
|
# Created: 18-APR-2005 18:12:20 TROJAN
|
|
# stdr 917
|
|
#
|
|
# Revision 1.11 (DELIVERED)
|
|
# Created: 02-APR-2005 17:47:24 TROJAN
|
|
# spr 6772
|
|
#
|
|
# Revision 1.10 (APPROVED)
|
|
# Created: 21-MAR-2005 15:47:11 TROJAN
|
|
# spr 6736
|
|
#
|
|
# Revision 1.9 (DELIVERED)
|
|
# Created: 02-FEB-2005 18:44:23 BLI
|
|
# Fixed a bug
|
|
#
|
|
# Revision 1.8 (APPROVED)
|
|
# Created: 27-JAN-2005 19:09:44 BLI
|
|
# Modified a module name
|
|
#
|
|
# Revision 1.7 (APPROVED)
|
|
# Created: 27-JAN-2005 18:35:22 BLI
|
|
# Modified to use new taf formatter
|
|
#
|
|
# Revision 1.6 (APPROVED)
|
|
# Created: 07-DEC-2004 19:25:38 TROJAN
|
|
# spr 6528
|
|
#
|
|
# Revision 1.5 (APPROVED)
|
|
# Created: 30-SEP-2004 20:53:43 TROJAN
|
|
# stdr 873
|
|
#
|
|
# Revision 1.4 (APPROVED)
|
|
# Created: 09-JUL-2004 19:11:12 OBERFIEL
|
|
# Replaced busy dialogs
|
|
#
|
|
# Revision 1.3 (APPROVED)
|
|
# Created: 01-JUL-2004 14:59:47 OBERFIEL
|
|
# Update
|
|
#
|
|
# Revision 1.2 (DELIVERED)
|
|
# Created: 08-JAN-2004 21:40:20 PCMS
|
|
# Updating for code cleanup
|
|
#
|
|
# Revision 1.1 (APPROVED)
|
|
# Created: 06-NOV-2003 16:46:15 OBERFIEL
|
|
# date and time created -2147483647/-2147483648/-2147481748
|
|
# -2147483648:-2147483648:-2147483648 by oberfiel
|
|
#
|
|
# Change Document History:
|
|
# 1:
|
|
# Change Document: GFS1-NHD_SPR_7417
|
|
# Action Date: 06-OCT-2009 09:42:01
|
|
# Relationship Type: In Response to
|
|
# Status: CLOSED
|
|
# Title: AvnFPS: TUG code does not handle transition from warm to cold seasons
|
|
#
|
|
#
|
|
|
|
import copy, logging, time
|
|
from Tkinter import *
|
|
import Pmw
|
|
from Balloon import *
|
|
import Avn, AvnLib, AvnParser, Globals, TafDecoder, TafGen
|
|
|
|
_Logger = logging.getLogger(__name__)
|
|
|
|
###############################################################################
|
|
class Viewer:
|
|
Format = ['table', 'long', 'short']
|
|
def __init__(self, master, tafviewer, **kw):
|
|
# master: parent widget (page in a notebook)
|
|
# mos: MOS class implementing methods:
|
|
# makeHeader(), makeRecords(), makeFMRec(),
|
|
# makeTEMPORec(), makeReport()
|
|
# tafviewer: TafViewer instance
|
|
self._master = master
|
|
self._id = None
|
|
self._ids = []
|
|
self._mosid = None
|
|
self._model = kw['model']
|
|
self._tafviewer = tafviewer
|
|
self._decoder = TafDecoder.Decoder()
|
|
self.messagebar = kw['messagebar']
|
|
|
|
self._tkShowProbs = IntVar()
|
|
self._tkShowProbs.set(int(master.option_get('showProbs', '')))
|
|
self._tkHighlightFlightCat = IntVar()
|
|
self._tkHighlightFlightCat.set(int(master.option_get( \
|
|
'highlightFlightCat', '')))
|
|
self._format = master.option_get('showFormatted', '')
|
|
if self._format not in self.Format:
|
|
self._format = self.Format[0]
|
|
self._tkBBB = StringVar()
|
|
self._tkBBB.set({'0':'AAA'}.get(master.option_get('showRoutine', ''),' '))
|
|
self._tkAll = IntVar()
|
|
|
|
frame = Frame(master)
|
|
cb = Checkbutton(frame,
|
|
text='Probabilities',
|
|
variable=self._tkShowProbs,
|
|
command=self.load,
|
|
)
|
|
cb.pack(side='right', padx=5)
|
|
Balloon().bind(cb, 'Display Vsby and Cig probabilities')
|
|
cb = Checkbutton(frame,
|
|
text='Flight Categories',
|
|
variable=self._tkHighlightFlightCat,
|
|
command=self.load,
|
|
)
|
|
cb.pack(side='right', padx=5)
|
|
Balloon().bind(cb, 'Background color depicts flight categories')
|
|
rbtns = Pmw.RadioSelect(frame,
|
|
buttontype = 'radiobutton',
|
|
labelpos='w',
|
|
label_text='Format',
|
|
orient='horizontal',
|
|
hull_borderwidth=2,
|
|
hull_relief='ridge',
|
|
command=self.__setformat,
|
|
)
|
|
rbtns.pack(side='right', padx=5)
|
|
for text in self.Format:
|
|
rbtns.add(text)
|
|
Balloon().bind(rbtns, 'Display format')
|
|
rbtns.setvalue(self._format)
|
|
cb = Checkbutton(frame,
|
|
text='Routine',
|
|
variable=self._tkBBB,
|
|
onvalue=' ',
|
|
offvalue='AAX',
|
|
command=self.load,
|
|
)
|
|
cb.pack(side='right', padx=5)
|
|
Balloon().bind(cb, 'Determines TAF issuance time')
|
|
cb = Checkbutton(frame,
|
|
text='All',
|
|
variable=self._tkAll,
|
|
command=self.load,
|
|
)
|
|
cb.pack(side='right', padx=5)
|
|
Balloon().bind(cb, 'Displays data for all sites')
|
|
frame.pack(side='top', expand='no', fill='x')
|
|
|
|
self.text = Pmw.ScrolledText(master,
|
|
columnheader=1,
|
|
columnheader_height=2,
|
|
columnheader_name='textViewer',
|
|
borderframe = 1,
|
|
vscrollmode='static',
|
|
hscrollmode='dynamic',
|
|
text_state='disabled',
|
|
text_wrap='none',
|
|
text_setgrid='true',
|
|
text_name='textViewer',
|
|
)
|
|
self.text.pack(side='top', expand='yes', fill='both')
|
|
self.text.component('text').bind('<Button-3>', self.__popupMenu)
|
|
|
|
self.popmenu = Menu(master,
|
|
tearoff=0,
|
|
type='normal',
|
|
)
|
|
self.popmenu.add_command(label='Copy', command=self.__copy)
|
|
|
|
AvnLib.configureFlightCatColors(self.text)
|
|
|
|
def __copy(self):
|
|
try:
|
|
t = self.text.component('text')
|
|
t.selection_own()
|
|
selection = t.selection_get()
|
|
t.clipboard_clear()
|
|
t.clipboard_append(selection)
|
|
except:
|
|
pass
|
|
|
|
def __popupMenu(self, e):
|
|
self.popmenu.tk_popup(e.widget.winfo_rootx() + e.x,
|
|
e.widget.winfo_rooty() + e.y)
|
|
|
|
def __makeHeader(self, data):
|
|
return '%s %s Guidance %s' % (self._model.upper()[:3],self._model.upper()[3:],
|
|
time.strftime('%m/%d/%y %H%M UTC',
|
|
time.gmtime(data['itime']['value'])))
|
|
|
|
def __showFormatted(self):
|
|
data = Globals.DRC.getMosData(self._mosid, self._model)
|
|
if data is None:
|
|
raise Avn.AvnError('Cannot retrieve data for %s' % self._mosid)
|
|
self.text.component('columnheader').insert('end',
|
|
self.__makeHeader(data.data))
|
|
tc = TafGen.TafGen(self._model,data.data,self._tkBBB.get())
|
|
donot_group = self._format == 'long'
|
|
taf=tc.createTaf(donot_group)
|
|
self.text.insert('end','\n'.join(taf))
|
|
if self._tkHighlightFlightCat.get():
|
|
dcd = self._decoder(taf)
|
|
if 'fatal' in dcd:
|
|
return
|
|
for ix, g in AvnLib.getTafPeriods(dcd):
|
|
fc = AvnLib.flightCategory(g)
|
|
self.text.tag_add(fc, *ix)
|
|
|
|
def __showTable(self):
|
|
data = Globals.DRC.getMosTable(self._mosid, self._model)
|
|
if data is None:
|
|
raise Avn.AvnError('Cannot retrieve data for %s' % self._mosid)
|
|
rpt = data.table.split('\n')
|
|
htw = self.text.component('columnheader')
|
|
htw.insert('end', '\n'.join(rpt[:2]))
|
|
if self._tkShowProbs.get():
|
|
self.text.insert('end', '\n'.join(rpt[2:]))
|
|
else:
|
|
self.text.insert('end', '\n'.join([x for x in rpt[2:] \
|
|
if not x.startswith('cat') and not x.startswith('PSKC') \
|
|
and not x.startswith('PFEW') and not x.startswith('PBKN') \
|
|
and not x.startswith('POVC') and not x.startswith('PSCT') \
|
|
and not x.startswith('PRain') and not x.startswith('PSnow') \
|
|
and not x.startswith('PFrz')]))
|
|
|
|
balloonMsgs = self.__createCategoryBalloons(self.text.getvalue().split('\n'))
|
|
for n, msg in enumerate(balloonMsgs):
|
|
tag = 'cat'+str(n)
|
|
self.text.tag_add(tag,*msg[1])
|
|
tagBalloon(relmouse='both',xoffset=10,yoffset=-15,pinned=True).tagbind(self.text,tag,msg[0])
|
|
|
|
if self._tkHighlightFlightCat.get():
|
|
data = Globals.DRC.getMosData(self._mosid, self._model)
|
|
if not data:
|
|
return
|
|
nrows = len(rpt) - 2
|
|
c = 9 # length of row label
|
|
for g in data.data['group']:
|
|
fc = AvnLib.flightCategory(g)
|
|
for n in range(1, nrows+1):
|
|
self.text.tag_add(fc, '%d.%d' % (n,c), '%d.%d' % (n,c+3))
|
|
c += 4 # column width + space
|
|
|
|
def __createCategoryBalloons(self,table):
|
|
"""Create informative balloon messages about MOS categories"""
|
|
balloonMsgs = []
|
|
if self._model[3:] not in ['mos','lamp']:
|
|
return balloonMsgs
|
|
|
|
visDict = { '1':'<1/2SM',
|
|
'2':'1/2-<1SM',
|
|
'3':'1-<2SM',
|
|
'4':'2-<3SM',
|
|
'5':'3-5SM',
|
|
'6':'6SM',
|
|
'7':'>6SM' }
|
|
|
|
cigDict = { '1':'<200ft',
|
|
'2':'200-400ft',
|
|
'3':'500-900ft',
|
|
'4':'1000-1900ft',
|
|
'5':'2000-3000ft',
|
|
'6':'3100-6500ft',
|
|
'7':'6600-12000ft',
|
|
'8':'>12000ft' }
|
|
|
|
qpfDict = { '1':'<0.1"',
|
|
'2':'0.1-<1/4"',
|
|
'3':'1/4-<1/2"',
|
|
'4':'1/2-<1"',
|
|
'5':'1-<2"',
|
|
'6':'>=2"' }
|
|
r = 0
|
|
for line in table:
|
|
r += 1
|
|
if line.startswith('VIS') or line.startswith('CVIS'):
|
|
lastCategory = 'VIS'
|
|
c = 9
|
|
llen=len(line.rstrip())
|
|
while c < llen:
|
|
index=line[c:c+3].strip()
|
|
if index:
|
|
balloonMsgs.append((visDict.get(index,'>6SM'),('%d.%d'%(r,c),'%d.%d'%(r,c+3))))
|
|
c += 4
|
|
elif line.startswith('CIG') or line.startswith('CCIG'):
|
|
lastCategory = 'CIG'
|
|
c = 9
|
|
llen=len(line.rstrip())
|
|
while c < llen:
|
|
index=line[c:c+3].strip()
|
|
if index:
|
|
balloonMsgs.append((cigDict.get(index,'Ulmtd'),('%d.%d'%(r,c),'%d.%d'%(r,c+3))))
|
|
c += 4
|
|
elif line.startswith('QPF06'):
|
|
c = 17
|
|
for fprd in line[9:].split():
|
|
balloonMsgs.append((qpfDict.get(fprd,'None'),('%d.%d'%(r,c),'%d.%d'%(r,c+3))))
|
|
c += 8
|
|
|
|
if line.startswith('cat'):
|
|
if lastCategory == 'VIS':
|
|
category=line[3]
|
|
balloonMsgs.append((visDict.get(line[3],'>6SM'),('%d.0'%r,'%d.4'%r)))
|
|
elif lastCategory == 'CIG':
|
|
balloonMsgs.append((cigDict.get(line[3],'Ulmtd'),('%d.0'%r,'%d.4'%r)))
|
|
|
|
return balloonMsgs
|
|
|
|
def __showAll(self):
|
|
donot_group = self._format == 'long'
|
|
bbb = self._tkBBB.get()
|
|
data = None
|
|
mosdata = Globals.DRC.getMosData(self._ids, self._model)
|
|
if mosdata:
|
|
for data in mosdata:
|
|
tc=TafGen.TafGen(self._model,data.data,bbb)
|
|
taf=tc.createTaf(donot_group)
|
|
offset = int(self.text.index('end').split('.')[0])-2
|
|
self.text.insert('end', '\n'.join(taf))
|
|
self.text.insert('end', '\n')
|
|
if self._tkHighlightFlightCat.get():
|
|
dcd = self._decoder(taf, bbb, offset)
|
|
if 'fatal' in dcd:
|
|
continue
|
|
for ix, g in AvnLib.getTafPeriods(dcd):
|
|
fc = AvnLib.flightCategory(g)
|
|
self.text.tag_add(fc, *ix)
|
|
else:
|
|
self.messagebar().message('usererror',"Insufficient Data for TAF Generation")
|
|
if data:
|
|
self.text.component('columnheader').insert('end',
|
|
self.__makeHeader(data.data))
|
|
|
|
def __setformat(self, tag):
|
|
self._format = tag
|
|
self.load()
|
|
|
|
###############################################################################
|
|
# public methods
|
|
###############################################################################
|
|
def load(self, arg=None):
|
|
self._tafviewer.load(self._id)
|
|
self.text.configure(text_state='normal')
|
|
self.text.clear()
|
|
htw = self.text.component('columnheader')
|
|
htw.configure(state='normal')
|
|
htw.delete('1.0', 'end')
|
|
msg = None
|
|
if self._tkAll.get():
|
|
if self._format == 'table':
|
|
msg = 'Table display for all sites is not supported'
|
|
else:
|
|
self.__showAll()
|
|
else:
|
|
if self._mosid:
|
|
try:
|
|
if self._format == 'table':
|
|
self.__showTable()
|
|
else:
|
|
self.__showFormatted()
|
|
except Avn.AvnError, e:
|
|
msg = str(e)
|
|
_Logger.warning(msg)
|
|
except Exception:
|
|
msg = 'Cannot load data for %s' % self._id
|
|
_Logger.exception(msg)
|
|
else:
|
|
msg = 'There is no MOS data for %s' % self._id
|
|
self.text.configure(text_state='disabled')
|
|
htw.configure(state='disabled')
|
|
if self._master.winfo_ismapped():
|
|
if msg:
|
|
self.messagebar().message('usererror', msg)
|
|
else:
|
|
self.messagebar().resetmessages('usererror')
|
|
|
|
def setSite(self, id_, ids):
|
|
if self._id == id_:
|
|
return
|
|
self._id = id_
|
|
self._mosid = None
|
|
self._ids = []
|
|
for i in ids:
|
|
siteinfo = AvnParser.getTafSiteCfg(i)
|
|
if siteinfo and siteinfo['sites'].get(self._model):
|
|
self._ids.extend(siteinfo['sites'][self._model])
|
|
if i == self._id:
|
|
self._mosid = self._ids[-1]
|