469 lines
15 KiB
Python
469 lines
15 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:
|
|
# AvnParser.py
|
|
# GFS1-NHD:A7852.0000-SCRIPT;1.23
|
|
#
|
|
# Status:
|
|
# DELIVERED
|
|
#
|
|
# History:
|
|
# Revision 1.23 (DELIVERED)
|
|
# Created: 30-JUN-2009 14:43:05 OBERFIEL
|
|
# Modifications to allow collective PIL to be written and
|
|
# read in TAF cfg files.
|
|
#
|
|
# Revision 1.22 (DELIVERED)
|
|
# Created: 15-MAY-2009 15:05:37 OBERFIEL
|
|
# Added afos id to dictionary returned by getTafHeaders
|
|
# routine.
|
|
#
|
|
# Revision 1.21 (DELIVERED)
|
|
# Created: 15-MAY-2008 15:10:56 GILMOREDM
|
|
# Now parses QC section from site config file
|
|
#
|
|
# Revision 1.20 (DELIVERED)
|
|
# Created: 17-MAR-2008 14:41:50 OBERFIEL
|
|
# Remove routines trying to read in TWB configuration files.
|
|
#
|
|
# Revision 1.19 (DELIVERED)
|
|
# Created: 04-MAY-2007 15:52:14 OBERFIEL
|
|
# Changes to reflect OB8.2/AvnFPS3.5 values and clean up in
|
|
# installation staging scripts. cvt3.py
|
|
# updated to obsolete fields and add new one, tafduration.
|
|
# AvnParser and DataRequestServ code cleaned up
|
|
# to remove references to avnmos, xtfs attributes and removed
|
|
# obsolete modules.
|
|
#
|
|
# Revision 1.18 (INITIALIZE)
|
|
# Created: 18-APR-2007 12:29:26 SOLSON
|
|
# Took out the CR characters from the previous rev of this
|
|
# file.
|
|
#
|
|
# Revision 1.17 (DELIVERED)
|
|
# Created: 06-DEC-2006 14:10:40 BLI
|
|
# Modified to make xmit configurable for each user
|
|
#
|
|
# Revision 1.16 (DELIVERED)
|
|
# Created: 23-MAR-2006 15:14:13 TROJAN
|
|
# spr 7109 - modified method to determine work PIL
|
|
#
|
|
# Revision 1.15 (DELIVERED)
|
|
# Created: 22-MAR-2006 13:05:14 TROJAN
|
|
# spr 7110. Revised TAF handling for consistency
|
|
#
|
|
# Revision 1.14 (DELIVERED)
|
|
# Created: 31-JAN-2006 18:09:31 TROJAN
|
|
# spr 7081
|
|
#
|
|
# Revision 1.13 (APPROVED)
|
|
# Created: 23-JAN-2006 08:23:11 TROJAN
|
|
# stdr 956
|
|
#
|
|
# Revision 1.12 (APPROVED)
|
|
# Created: 30-NOV-2005 11:42:52 OBERFIEL
|
|
# Updated code based on review
|
|
#
|
|
# Revision 1.11 (DELIVERED)
|
|
# Created: 09-SEP-2005 19:05:47 TROJAN
|
|
# spr 7012
|
|
#
|
|
# Revision 1.10 (DELIVERED)
|
|
# Created: 06-JUL-2005 18:16:33 TROJAN
|
|
# spr 6548
|
|
#
|
|
# Revision 1.9 (DELIVERED)
|
|
# Created: 01-JUN-2005 17:40:58 OBERFIEL
|
|
# Bug fixes since RHE3 initial snapshot
|
|
#
|
|
# Revision 1.8 (DELIVERED)
|
|
# Created: 07-MAY-2005 11:29:59 OBERFIEL
|
|
# Added Item Header Block
|
|
#
|
|
# Revision 1.7 (DELIVERED)
|
|
# Created: 18-APR-2005 17:31:34 OBERFIEL
|
|
# Changes to support gamin
|
|
#
|
|
# Revision 1.6 (DELIVERED)
|
|
# Created: 02-APR-2005 17:02:15 TROJAN
|
|
# spr 6763
|
|
#
|
|
# Revision 1.5 (DELIVERED)
|
|
# Created: 24-JAN-2005 15:51:12 TROJAN
|
|
# spr 6259
|
|
#
|
|
# Revision 1.4 (APPROVED)
|
|
# Created: 30-SEP-2004 18:56:01 TROJAN
|
|
# stdr 874
|
|
#
|
|
# Revision 1.3 (APPROVED)
|
|
# Created: 19-AUG-2004 20:20:45 OBERFIEL
|
|
# Fixed
|
|
#
|
|
# Revision 1.2 (REVIEW)
|
|
# Created: 09-JUL-2004 19:28:38 OBERFIEL
|
|
# Added climate QC
|
|
#
|
|
# Revision 1.1 (APPROVED)
|
|
# Created: 01-JUL-2004 16:44:08 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
|
|
#
|
|
#
|
|
##
|
|
# SOFTWARE HISTORY
|
|
#
|
|
# Date Ticket# Engineer Description
|
|
# ------------- -------- --------- --------------------------------------------
|
|
# Mar 10, 2022 8808 randerso Update ConfigParser to better work with
|
|
# Java commons.configuration
|
|
#
|
|
##
|
|
# This is a base file that is not intended to be overridden.
|
|
##
|
|
|
|
import logging
|
|
import os
|
|
|
|
import Avn
|
|
import AvnConfigParser
|
|
|
|
_Logger = logging.getLogger(Avn.CATEGORY)
|
|
|
|
_ServerCfg = os.path.join('etc', 'server.cfg')
|
|
_GuiCfg = os.path.join('etc', 'gui.cfg')
|
|
_PlotCfg = os.path.join('etc', 'wxplot.cfg')
|
|
ForecasterFile = os.path.join('etc', 'forecasters')
|
|
|
|
|
|
def _guess(arg):
|
|
'''Tries to guess and convert arg, passed as a string'''
|
|
if not isinstance(arg, str):
|
|
return
|
|
try:
|
|
return int(arg)
|
|
except ValueError:
|
|
try:
|
|
return float(arg)
|
|
except ValueError:
|
|
return arg
|
|
|
|
|
|
def _getProducts(direct):
|
|
"""Returns *.cfg file names with .cfg stripped
|
|
Default is first on the list
|
|
"""
|
|
try:
|
|
plist = sorted(os.path.splitext(f)[0]
|
|
for f in os.listdir(direct)
|
|
if f.endswith('.cfg')
|
|
)
|
|
with open(os.path.join(direct, 'DEFAULT')) as f:
|
|
default = f.read().rstrip()
|
|
plist.remove(default)
|
|
return [default] + plist
|
|
except (IOError, ValueError):
|
|
return plist
|
|
except OSError:
|
|
_Logger.exception('Error accessing products in %s', direct)
|
|
return []
|
|
|
|
|
|
def _tafProdFilename(product):
|
|
direct = Avn.PATH_MGR.getStaticFile(os.path.join(Avn.ConfigDir, 'tafs'))
|
|
return os.path.join(direct.getPath(), product) + '.cfg'
|
|
|
|
|
|
###############################################################################
|
|
def split(strng, sep=','):
|
|
'''Splits strng into sep (default: a comma) delineated chunks.
|
|
Strips leading and trailing blanks'''
|
|
return [_f for _f in [s.strip() for s in strng.split(sep)] if _f]
|
|
|
|
|
|
def getClimQCConfig(ident):
|
|
# file containing category definitions: thresholds
|
|
fname = Avn.getTafPath(ident, 'climqc.cfg')
|
|
cp = AvnConfigParser.AvnConfigParser()
|
|
cp.read(fname)
|
|
d = dict([(t, [_guess(x) for x in cp.get('thresholds', t).split(',')])
|
|
for t in ['vsby', 'cig', 'ff', 'dd']])
|
|
opts = dict(cp.items('args'))
|
|
for k in opts:
|
|
opts[k] = _guess(opts[k])
|
|
d.update(opts)
|
|
return d
|
|
|
|
|
|
def getServerCfg():
|
|
try:
|
|
if not os.path.isfile(_ServerCfg):
|
|
raise Avn.AvnError('File %s does not exist' % _ServerCfg)
|
|
cp = AvnConfigParser.AvnConfigParser()
|
|
cp.read(_ServerCfg)
|
|
d = {}
|
|
tags = split(cp.get('dis', 'tags'))
|
|
d['dis'] = [dict(cp.items('dis_' + tag)) for tag in tags]
|
|
# access control
|
|
d['valid'] = split(cp.get('valid', 'hosts'))
|
|
return d
|
|
except Exception:
|
|
msg = 'Cannot process %s' % _ServerCfg
|
|
_Logger.exception(msg)
|
|
return None
|
|
|
|
|
|
def getGuiCfg():
|
|
try:
|
|
if not os.path.isfile(_GuiCfg):
|
|
raise Avn.AvnError('File %s does not exist' % _GuiCfg)
|
|
cp = AvnConfigParser.AvnConfigParser()
|
|
cp.read(_GuiCfg)
|
|
d = {'features': {}}
|
|
# miscellaneous
|
|
for opt in cp.options('features'):
|
|
d['features'][opt] = cp.getboolean('features', opt)
|
|
# servers
|
|
d['ns'] = split(cp.get('ns', 'tags'))
|
|
# colors
|
|
d['colors'] = split(cp.get('colors', 'tags'))
|
|
# editor tags
|
|
d['edittags'] = dict(cp.items('editor_tags'))
|
|
# monitors
|
|
n = int(cp.get('menus', 'number'))
|
|
d['menus'] = [split(cp.get('menu_' + str(k), 'items')) for k in range(n)]
|
|
allitems = {}
|
|
for items in d['menus']:
|
|
allitems.update(dict.fromkeys(items))
|
|
d['monitors'] = {}
|
|
for tag in allitems:
|
|
opts = dict(cp.items('monitor_' + tag))
|
|
for k in opts:
|
|
if k in ['items', 'labels']:
|
|
opts[k] = [_f for _f in [x.strip() for x in
|
|
opts[k].split(',')] if _f]
|
|
else:
|
|
opts[k] = _guess(opts[k])
|
|
opts['labels'] = dict(zip(opts['items'], opts['labels']))
|
|
d['monitors'][tag] = opts
|
|
# make label dictionary from list
|
|
# viewers - used in the editor
|
|
d['viewers'] = []
|
|
for tag in split(cp.get('viewers', 'tags')):
|
|
opts = dict(cp.items('viewer_' + tag))
|
|
m = __import__(opts['module'])
|
|
del opts['module']
|
|
d['viewers'].append(Avn.Bunch(tag=tag, module=m, args=opts))
|
|
return d
|
|
except Exception:
|
|
msg = 'Cannot process %s' % _GuiCfg
|
|
_Logger.exception(msg)
|
|
return None
|
|
|
|
|
|
def getPlotCfg():
|
|
d = {'hours': {}, 'cig': {}, 'vsby': {}, 'viewers': []}
|
|
try:
|
|
if not os.path.isfile(_PlotCfg):
|
|
raise Avn.AvnError('File %s does not exist' % _PlotCfg)
|
|
cp = AvnConfigParser.AvnConfigParser()
|
|
cp.read(_PlotCfg)
|
|
for option in cp.options('vsby'):
|
|
d['vsby'][option] = cp.getfloat('vsby', option)
|
|
for option in cp.options('cig'):
|
|
d['cig'][option] = cp.getint('cig', option)
|
|
for option in cp.options('hours'):
|
|
d['hours'][option] = cp.getint('hours', option)
|
|
for tag in split(cp.get('viewers', 'tags')):
|
|
opts = dict(cp.items('viewer_' + tag))
|
|
m = __import__(opts['module'])
|
|
del opts['module']
|
|
l = opts['label']
|
|
del opts['label']
|
|
d['viewers'].append(Avn.Bunch(tag=tag, label=l, module=m,
|
|
args=opts))
|
|
d['selected'] = split(cp.get('selected', 'tags'))
|
|
d['printcmd'] = cp.get('print', 'cmd')
|
|
return d
|
|
except Exception:
|
|
msg = 'Cannot process %s' % _PlotCfg
|
|
_Logger.exception(msg)
|
|
return None
|
|
|
|
|
|
def getTafProductCfg(product):
|
|
try:
|
|
fname = _tafProdFilename(product)
|
|
if not os.path.isfile(fname):
|
|
raise Avn.AvnError('File %s does not exist' % fname)
|
|
cp = AvnConfigParser.AvnConfigParser()
|
|
cp.read(fname)
|
|
d = {'sites': split(cp.get('sites', 'idents'))}
|
|
if cp.has_option('sites', 'workpil'):
|
|
d['workpil'] = cp.get('sites', 'workpil')
|
|
else:
|
|
d['workpil'] = Avn.TAFWorkPIL
|
|
if cp.has_option('sites', 'collective'):
|
|
d['collective'] = cp.get('sites', 'collective')
|
|
return d
|
|
except Exception as ex:
|
|
msg = 'Cannot get configuration for %s: %s' % (product, str(ex))
|
|
_Logger.exception(msg)
|
|
return None
|
|
|
|
|
|
def getTafSiteCfg(ident):
|
|
try:
|
|
fname = os.path.join(Avn.ConfigDir, 'tafs', ident, 'info.cfg')
|
|
f = Avn.PATH_MGR.getStaticFile(fname)
|
|
if f:
|
|
fname = f.getPath()
|
|
if not os.path.isfile(fname):
|
|
raise Avn.AvnError('File %s does not exist' % fname)
|
|
cp = AvnConfigParser.AvnConfigParser()
|
|
cp.read(fname)
|
|
d = {'ident': ident}
|
|
d['headers'] = dict(cp.items('headers'))
|
|
d['geography'] = dict(cp.items('geography'))
|
|
d['geography']['runway'] = [int(x) for x in
|
|
split(d['geography']['runway'])]
|
|
d['sites'] = {}
|
|
for key, value in cp.items('sites'):
|
|
d['sites'][key] = split(value)
|
|
d['thresholds'] = {}
|
|
vsby = [float(x) for x in split(cp.get('thresholds', 'vsby'))]
|
|
d['thresholds']['vsby'] = vsby
|
|
cig = [int(x) for x in split(cp.get('thresholds', 'cig'))]
|
|
d['thresholds']['cig'] = cig
|
|
if 'radar_cutoff' in cp.options('thresholds'):
|
|
cutoffs = [int(x) for x in split(cp.get('thresholds',
|
|
'radar_cutoff'))]
|
|
else:
|
|
cutoffs = [0] * len(d['sites'].get('radars', []))
|
|
d['thresholds']['radar_cutoff'] = cutoffs
|
|
if 'profiler_cutoff' in cp.options('thresholds'):
|
|
cutoffs = [int(x) for x in split(cp.get('thresholds',
|
|
'profiler_cutoff'))]
|
|
else:
|
|
cutoffs = [0] * len(d['sites'].get('profilers', []))
|
|
|
|
d['thresholds']['profiler_cutoff'] = cutoffs
|
|
|
|
d['thresholds']['tafduration'] = cp.get('thresholds', 'tafduration')
|
|
d['qc'] = {'currentwx':1, 'climate':1, 'impact':1}
|
|
|
|
if 'qc' in cp.sections():
|
|
for x in ['currentwx', 'impact', 'climate']:
|
|
if x in cp.options('qc'):
|
|
d['qc'][x] = int(cp.get('qc', x))
|
|
|
|
return d
|
|
|
|
except Exception:
|
|
msg = 'Cannot process %s' % fname
|
|
_Logger.exception(msg)
|
|
return None
|
|
|
|
|
|
def getTafTemplate(ident, hour):
|
|
fpath = os.path.join('etc', 'tafs', ident, '%02d.template' % hour)
|
|
with open(fpath) as f:
|
|
return f.read()
|
|
|
|
|
|
def getTafProducts():
|
|
direct = Avn.PATH_MGR.getStaticFile(os.path.join(Avn.ConfigDir, 'tafs'))
|
|
#return _getProducts(os.path.join('etc', 'tafs'))
|
|
return _getProducts(direct.getPath())
|
|
|
|
|
|
def getForecasters():
|
|
# Returns list of forecasters as a dictionary (name, id)
|
|
d = {}
|
|
with open(ForecasterFile) as f:
|
|
for line in f:
|
|
try:
|
|
if line[0] == '#':
|
|
continue
|
|
ident, xmit, name = line.split(None, 2)
|
|
d[name.rstrip()] = {'id':int(ident), 'xmit':int(xmit)}
|
|
except (IndexError, ValueError):
|
|
pass
|
|
return d
|
|
|
|
|
|
def getTafHeaders():
|
|
# Returns dictionary of awips and wmo headers for all TAF sites
|
|
tmp = []
|
|
plist = getTafProducts()
|
|
for p in plist:
|
|
sitelist = getTafProductCfg(p)['sites']
|
|
if sitelist is not None:
|
|
tmp.extend(sitelist)
|
|
d = dict.fromkeys(tmp)
|
|
for ident in d:
|
|
siteCfg = getTafSiteCfg(ident)
|
|
if siteCfg is None:
|
|
del d[ident]
|
|
continue
|
|
header = siteCfg['headers']
|
|
awipsid = header['wmo'].split()[1] + header['afos'][3:]
|
|
d[ident] = {'wmo': header['wmo'],
|
|
'awips': awipsid,
|
|
'afos': header['afos']
|
|
}
|
|
|
|
return d
|
|
|
|
|
|
def getAllSiteIds():
|
|
iddict = {}
|
|
# read all TAF configuration files
|
|
tafids = {}
|
|
for prod in getTafProducts():
|
|
ids = getTafProductCfg(prod)['sites']
|
|
if ids:
|
|
tafids.update(dict.fromkeys(ids))
|
|
iddict['taf'] = {}
|
|
for ident in tafids:
|
|
cfg = getTafSiteCfg(ident)
|
|
if cfg is None:
|
|
continue
|
|
iddict['taf'][ident] = None
|
|
for tag in cfg['sites']:
|
|
if tag not in iddict:
|
|
iddict[tag] = {}
|
|
i = cfg['sites'][tag]
|
|
if type(i) == type([]):
|
|
for item in i:
|
|
iddict[tag][item] = None
|
|
else:
|
|
iddict[tag][i] = None
|
|
|
|
return {k: sorted(v) for (k, v) in iddict.items()}
|