From 0de24e8a44e4068c5491b8513301bb71f4f1b711 Mon Sep 17 00:00:00 2001 From: David Gillingham Date: Wed, 14 May 2014 16:51:22 -0500 Subject: [PATCH] Omaha #3157: Refactor TPCWatchSrv, SPCWatchSrv, and WCLWatchSrv to correctly handle multiple active GFE sites. Update active table code to allow multiple configured SPC and TPC sites from VTECPartners.py. Change-Id: Ie7565f0932054477580baf7e06e26357916fd746 Former-commit-id: 04557f38544ce77a3d14aaa2c6c73e8a402de0c2 [formerly 9185eea7409452fe004185dfa51d78f5adb656d4] Former-commit-id: 1f1db083ca067abe153e6d8e407d2ae1c32e39d6 --- .../res/spring/gfe-spring.xml | 10 +- .../plugin/gfe/config/GFESiteActivation.java | 6 + .../edex/plugin/gfe/spc/SPCWatchSrv.java | 151 ----------- .../edex/plugin/gfe/tpc/TPCWatchSrv.java | 205 --------------- .../gfe/watch/AbstractWatchNotifierSrv.java | 173 +++++++++++++ .../edex/plugin/gfe/watch/SPCWatchSrv.java | 118 +++++++++ .../edex/plugin/gfe/watch/TPCWatchSrv.java | 151 +++++++++++ .../gfe/{wcl => watch}/WCLWatchSrv.java | 234 +++++++----------- .../plugin/gfe/watch/WatchProductUtil.java | 96 +++++++ .../plugin/gfe/{wcl => watch}/WclInfo.java | 2 +- .../WarningDecoder.py | 3 +- .../META-INF/MANIFEST.MF | 8 +- .../uf/common/activetable/VTECPartners.java | 58 ++++- .../uf/edex/activetable/ActiveTable.java | 10 +- .../common_static/base/vtec/ActiveTable.py | 9 +- .../common_static/base/vtec/MergeVTEC.py | 11 +- .../common_static/base/vtec/VTECPartners.py | 4 +- .../common_static/base/vtec/requestAT.py | 12 +- .../utility/common_static/base/vtec/sendAT.py | 12 +- 19 files changed, 737 insertions(+), 536 deletions(-) delete mode 100644 edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/spc/SPCWatchSrv.java delete mode 100644 edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/tpc/TPCWatchSrv.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/AbstractWatchNotifierSrv.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/SPCWatchSrv.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/TPCWatchSrv.java rename edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/{wcl => watch}/WCLWatchSrv.java (65%) create mode 100644 edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/WatchProductUtil.java rename edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/{wcl => watch}/WclInfo.java (98%) diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/res/spring/gfe-spring.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/res/spring/gfe-spring.xml index 88ed5393f0..a1ce8ec0ea 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/res/spring/gfe-spring.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/res/spring/gfe-spring.xml @@ -24,9 +24,9 @@ - - - + + + @@ -36,7 +36,7 @@ - + java.lang.Throwable - + java.lang.Throwable * * @author njensen @@ -430,8 +431,13 @@ public class GFESiteActivation implements ISiteActivationListener { * Returns the currently active GFE sites the server is running * * @return the active sites + * + * @deprecated It is preferred that you use the method + * {@link IFPServer#getActiveSites()} to retrieve the list of + * GFE active sites. */ @Override + @Deprecated public Set getActiveSites() { return IFPServerConfigManager.getActiveSites(); } diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/spc/SPCWatchSrv.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/spc/SPCWatchSrv.java deleted file mode 100644 index e174fe2531..0000000000 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/spc/SPCWatchSrv.java +++ /dev/null @@ -1,151 +0,0 @@ -/** - * 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. - **/ -package com.raytheon.edex.plugin.gfe.spc; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.raytheon.edex.plugin.gfe.config.GFESiteActivation; -import com.raytheon.edex.plugin.gfe.util.SendNotifications; -import com.raytheon.uf.common.activetable.VTECPartners; -import com.raytheon.uf.common.dataplugin.PluginDataObject; -import com.raytheon.uf.common.dataplugin.gfe.server.notify.UserMessageNotification; -import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord; -import com.raytheon.uf.common.status.UFStatus.Priority; -import com.raytheon.uf.edex.core.EdexException; -import com.raytheon.uf.edex.core.props.EnvProperties; -import com.raytheon.uf.edex.core.props.PropertiesFactory; - -/** - * Watches ingested warnings for WOU products from the SPC (Storm Prediction - * Center). If the warning is a WOU, then it looks to see if the site is in the - * ATTN...WFO... line, and if so, sends a user message to GFE to alert users. - * - *
- * SOFTWARE HISTORY
- * Date         Ticket#    Engineer    Description
- * ------------ ---------- ----------- --------------------------
- * Oct 3, 2008            njensen     Initial creation
- * Jul 10, 2009  #2590 njensen     Added multiple site support
- * 
- * - * @author njensen - * @version 1.0 - */ - -public class SPCWatchSrv { - - private static final Pattern ATTN_WFO = Pattern - .compile("ATTN\\.\\.\\.WFO\\.\\.\\.([A-Z]{3}\\.\\.\\.)+"); - - protected transient Log logger = LogFactory.getLog(getClass()); - - public void handleSpcWatch(List pdos) - throws EdexException { - // create the appropriate SPC notification, returns null if not - // needed. - EnvProperties env = PropertiesFactory.getInstance().getEnvProperties(); - String primarySite = env.getEnvValue("SITENAME"); - String spcSite = (String) VTECPartners.getInstance(primarySite) - .getattr("VTEC_SPC_SITE", "KWNS"); - for (PluginDataObject pdo : pdos) { - AbstractWarningRecord warn = (AbstractWarningRecord) pdo; - if (!warn.getPil().equals("WOU")) { - logger.debug("SPC notification: not WOU product"); - return; - } - - // find the first record from KWNS, SV.A, TO.A in this product - // action code must be "NEW" - if (warn.getOfficeid().equals(spcSite) - && warn.getSig().equals("A") - && (warn.getPhen().equals("TO") || warn.getPhen().equals( - "SV")) && warn.getAct().equals("NEW")) { - // decode the ATTN line, which tells us which WFOs are affected - List wfos = getAttnWfos(warn.getRawmessage()); - for (String siteid : GFESiteActivation.getInstance() - .getActiveSites()) { - if (!wfos.contains(siteid)) { - logger.debug("SPC notification: my site not in ATTN list"); - continue; // not my WFO - } - - // create the message - String txt = ""; - if (warn.getPhen().equals("TO")) { - txt = "Tornado Watch"; - } else if (warn.getPhen().equals("SV")) { - txt = "Severe Thunderstorm Watch"; - } - - String testText = ""; - if (warn.getVtecstr().charAt(1) == 'T') { - testText = " This is a TEST watch. Please restart the GFE " - + "in TEST mode before issuing WCN. "; - } - String msg = "Alert: " - + txt - + " " - + warn.getEtn() - + " has arrived. " - + "Check for 'red' locks (owned by others) on your Hazard grid and resolve them. " - + "If hazards are separated into temporary grids, please run MergeHazards. " - + "Next...save Hazards grid. Finally, select PlotSPCWatches from the Hazards menu."; - msg = msg + testText; - - UserMessageNotification notification = new UserMessageNotification( - msg, Priority.CRITICAL, "GFE", siteid); - SendNotifications.send(notification); - } - } else { - logger.debug("SPC notification: " - + "no SV.A, TO.A vtec lines, or not NEW action code"); - } - } - - } - - private static List getAttnWfos(String rawMessage) { - List list = new ArrayList(); - - // decode the ATTN line, which tells us which WFOs are affected - // only used for WCL and WOU products - Matcher m = ATTN_WFO.matcher(rawMessage); - if (m.find()) { - String found = m.group(); - // eliminate ATTN...WFO... - found = found.substring(13); - if (found != null) { - String[] wfos = found.split("\\.\\.\\."); - for (String s : wfos) { - list.add(s); - } - } - } - - return list; - } - -} diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/tpc/TPCWatchSrv.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/tpc/TPCWatchSrv.java deleted file mode 100644 index 907ccd9bbc..0000000000 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/tpc/TPCWatchSrv.java +++ /dev/null @@ -1,205 +0,0 @@ -/** - * 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. - **/ -package com.raytheon.edex.plugin.gfe.tpc; - -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.raytheon.edex.plugin.gfe.config.GFESiteActivation; -import com.raytheon.edex.plugin.gfe.util.SendNotifications; -import com.raytheon.uf.common.activetable.VTECPartners; -import com.raytheon.uf.common.dataplugin.PluginDataObject; -import com.raytheon.uf.common.dataplugin.gfe.server.notify.UserMessageNotification; -import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord; -import com.raytheon.uf.common.status.UFStatus.Priority; -import com.raytheon.uf.edex.core.EdexException; -import com.raytheon.uf.edex.core.props.EnvProperties; -import com.raytheon.uf.edex.core.props.PropertiesFactory; - -/** - * Watches ingested warnings for WOU products from the SPC (Storm Prediction - * Center). If the warning is a WOU, then it looks to see if the site is in the - * ATTN...WFO... line, and if so, sends a user message to GFE to alert users. - * - *
- * SOFTWARE HISTORY
- * Date         Ticket#    Engineer    Description
- * ------------ ---------- ----------- --------------------------
- * Oct 3, 2008            njensen     Initial creation
- * Jul 10, 2009  #2590 njensen     Added multiple site support
- * 
- * - * @author njensen - * @version 1.0 - */ - -public class TPCWatchSrv { - - private static final Pattern ATTN_WFO = Pattern - .compile("ATTN\\.\\.\\.WFO\\.\\.\\.([A-Z]{3}\\.\\.\\.)+"); - - private static final Map phensigMap; - - private static final Map actMap; - - static { - Map phensigMapTemp = new HashMap(); - phensigMapTemp.put("HU.A", "Hurricane Watch"); - phensigMapTemp.put("HU.S", "Hurricane Local Statement"); - phensigMapTemp.put("HU.W", "Hurricane Warning"); - phensigMap = Collections.unmodifiableMap(phensigMapTemp); - - Map actMapTemp = new HashMap(); - actMapTemp.put("CON", "Continued"); - actMapTemp.put("CAN", "Cancelled"); - actMapTemp.put("NEW", "New"); - actMap = Collections.unmodifiableMap(actMapTemp); - } - - private static final String alertTxt = "Alert: {0} has arrived from TPC. " - + "Check for 'red' locks (owned by others) on your Hazard grid and resolve them. " - + "If hazards are separated into temporary grids, please run Mergehazards. " - + "Next...save Hazards grid. Finally, select PlotTPCEvents from Hazards menu."; - - protected transient Log logger = LogFactory.getLog(getClass()); - - public void handleTpcWatch(List pdos) - throws EdexException { - - EnvProperties env = PropertiesFactory.getInstance().getEnvProperties(); - String primarySite = env.getEnvValue("SITENAME"); - String tpcSite = (String) VTECPartners.getInstance(primarySite) - .getattr("VTEC_TPC_SITE", "KNHC"); - Set activeSites = GFESiteActivation.getInstance() - .getActiveSites(); - - AbstractWarningRecord ourWarn = null; - String ourSite = null; - // create the appropriate TPC notification, returns null if not - // needed. - Map> phensigStormAct = new HashMap>(); - for (PluginDataObject pdo : pdos) { - AbstractWarningRecord warn = (AbstractWarningRecord) pdo; - if (!warn.getPil().startsWith("TCV")) { - logger.debug("TPC notification: not TCV product"); - return; - } - - // The warning is a TPC, but for us? - List wfos = getAttnWfos(warn.getRawmessage()); - wfos.retainAll(activeSites); - if (wfos.size() == 0) { - logger.debug("TPC notification: my site not in ATTN list"); - continue; - } - - if (ourWarn == null) { - ourWarn = warn; - ourSite = wfos.get(0); - } - - // Collect action codes by phensig and storm # - if (tpcSite.equals(warn.getOfficeid())) { - String phensig = warn.getPhen() + "." + warn.getSig(); - String storm = warn.getEtn(); - String act = warn.getAct(); - Set psActs = phensigStormAct.get(phensig + ":" + storm); - if (psActs == null) { - psActs = new TreeSet(); - phensigStormAct.put(phensig + ":" + storm, psActs); - } - psActs.add(act); - } - - } - - if (phensigStormAct.size() == 0) { - logger.debug("TPC Notification: no HU/TR vtec lines, or not NEW action code"); - return; - } - - // Build the notification message - StringBuilder msg = new StringBuilder(); - msg.append(MessageFormat.format(alertTxt, ourWarn.getPil())); - - for (String phensigStorm : phensigStormAct.keySet()) { - Collection acts = phensigStormAct.get(phensigStorm); - String[] splitKey = phensigStorm.split(":"); - String phensig = splitKey[0]; - String storm = splitKey[1]; - - String t1 = phensigMap.get(phensig); - if (t1 == null) { - t1 = phensig; - } - msg.append(t1 + ": #" + storm + "("); - String sep = ""; - for (String a : acts) { - String a1 = actMap.get(a); - if (a1 == null) { - a1 = a; - } - msg.append(sep).append(a1); - sep = ","; - } - msg.append("). "); - } - - UserMessageNotification notification = new UserMessageNotification( - msg.toString(), Priority.CRITICAL, "GFE", ourSite); - - SendNotifications.send(notification); - - } - - private static List getAttnWfos(String rawMessage) { - List list = new ArrayList(); - - // decode the ATTN line, which tells us which WFOs are affected - // only used for WCL and WOU products - Matcher m = ATTN_WFO.matcher(rawMessage); - if (m.find()) { - String found = m.group(); - // eliminate ATTN...WFO... - found = found.substring(13); - if (found != null) { - String[] wfos = found.split("\\.\\.\\."); - for (String s : wfos) { - list.add(s); - } - } - } - - return list; - } - -} diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/AbstractWatchNotifierSrv.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/AbstractWatchNotifierSrv.java new file mode 100644 index 0000000000..dabbfbaf89 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/AbstractWatchNotifierSrv.java @@ -0,0 +1,173 @@ +/** + * 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. + **/ +package com.raytheon.edex.plugin.gfe.watch; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.raytheon.edex.plugin.gfe.server.IFPServer; +import com.raytheon.edex.plugin.gfe.util.SendNotifications; +import com.raytheon.uf.common.activetable.VTECPartners; +import com.raytheon.uf.common.dataplugin.PluginDataObject; +import com.raytheon.uf.common.dataplugin.gfe.server.message.ServerResponse; +import com.raytheon.uf.common.dataplugin.gfe.server.notify.GfeNotification; +import com.raytheon.uf.common.dataplugin.gfe.server.notify.UserMessageNotification; +import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.common.status.UFStatus.Priority; + +/** + * Base class for a bean that accepts a {@code List} of + * {@code AbstractWarningRecord}s and generates a set of notifications for the + * GFE user if the storm affects their WFO. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * May 12, 2014  #3157     dgilling     Initial creation
+ * 
+ * 
+ * + * @author dgilling + * @version 1.0 + */ + +public abstract class AbstractWatchNotifierSrv { + + protected final IUFStatusHandler statusHandler = UFStatus + .getHandler(getClass()); + + protected final String watchType; + + protected final String supportedPIL; + + protected AbstractWatchNotifierSrv(String watchType, String supportedPIL) { + this.watchType = watchType; + this.supportedPIL = supportedPIL; + } + + /** + * Processes the warning records and generates a notification for each + * currently activated GFE site if the storm affects the site. + * + * @param pdos + * A list of {@code PluginDataObject}s that are assumed to be + * {@code AbstractWarningRecord}s all decoded from a common + * warning product. + */ + public final void handleWatch(List pdos) { + List warningRecs = filterIncomingRecordsByPIL(pdos); + if (warningRecs.isEmpty()) { + String logMsg = String.format("%s notification: not %s product", + watchType, supportedPIL); + statusHandler.debug(logMsg); + return; + } + + /* + * We are making an assumption that all PDOs came from the same source + * product. This is a safe assumption because WarningDecoder processes + * records one product at a time and the plugin notifier code that sends + * those records to us does not do any additional grouping or batching. + * + * Hence, any of the remaining records' raw message will be the same as + * the rest and we can just use the first record's copy. + */ + String productText = warningRecs.get(0).getRawmessage(); + Collection wfos = WatchProductUtil.findAttnWFOs(productText); + + for (String siteid : IFPServer.getActiveSites()) { + if (!wfos.contains(siteid)) { + String logMsg = String.format( + "%s notification: my site %s not in ATTN list", + watchType, siteid); + statusHandler.debug(logMsg); + continue; + } + + VTECPartners partnersConfig = VTECPartners.getInstance(siteid); + + String msg = buildNotification(warningRecs, partnersConfig); + if (msg != null) { + sendNotification(msg, siteid); + } + } + } + + /** + * Given a list of {@code PluginDataObject}s that are actually + * {@code AbstractWarningRecord}s, filters the list for only those records + * which have the right PIL code. + * + * @param pdos + * List of {@code AbstractWarningRecord}s to filter. + * @return The list of supported {@code AbstractWarningRecord}s as defined + * by {@code getSupportedPIL}. + */ + protected List filterIncomingRecordsByPIL( + List pdos) { + List warningRecords = new ArrayList(); + for (PluginDataObject pdo : pdos) { + AbstractWarningRecord warning = (AbstractWarningRecord) pdo; + + if (warning.getPil().startsWith(supportedPIL)) { + warningRecords.add(warning); + } + } + + return warningRecords; + } + + /** + * Takes the specified list of warning records and {@code VTECPartners} + * configuration data and builds a notification message to send. + * + * @param decodedVTEC + * The warning records. + * @param partnersConfig + * The {@code VTECPartners} configuration data. + * @return The notification message to send to the users for the site, or + * {@code null} if no notification applies. + */ + protected abstract String buildNotification( + List decodedVTEC, VTECPartners partnersConfig); + + /** + * Sends the specified notification message as an AletViz alert to all GFE + * users connected as the specified site. + * + * @param message + * The notification message text to send. + * @param siteId + * The site identifier that will receive the message. + * @return A {@code ServerResponse} containing error message if sending the + * notification message failed. + */ + protected ServerResponse sendNotification(String message, String siteId) { + GfeNotification notification = new UserMessageNotification(message, + Priority.CRITICAL, "GFE", siteId); + return SendNotifications.send(notification); + } +} diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/SPCWatchSrv.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/SPCWatchSrv.java new file mode 100644 index 0000000000..a12625a3a0 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/SPCWatchSrv.java @@ -0,0 +1,118 @@ +/** + * 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. + **/ +package com.raytheon.edex.plugin.gfe.watch; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.raytheon.uf.common.activetable.VTECPartners; +import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord; + +/** + * Watches ingested warnings for WOU products from the SPC (Storm Prediction + * Center). If the warning is a WOU, then it looks to see if the site is in the + * ATTN...WFO... line, and if so, sends a user message to GFE to alert users. + * + *
+ * SOFTWARE HISTORY
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Oct 03, 2008            njensen      Initial creation
+ * Jul 10, 2009  #2590     njensen      Added multiple site support
+ * May 12, 2014  #3157     dgilling     Re-factor based on AbstractWatchNotifierSrv.
+ * 
+ * + * @author njensen + * @version 1.0 + */ + +public final class SPCWatchSrv extends AbstractWatchNotifierSrv { + + private static final String SPC_WATCH_TYPE = "SPC"; + + private static final String SPC_SUPPORTED_PIL = "WOU"; + + private static final String DEFAULT_SPC_SITE = "KNHC"; + + private static final String TEST_TEXT_MSG = " This is a TEST watch. Please restart the GFE in TEST mode before issuing WCN. "; + + private static final String ALERT_MSG = "Alert: %s %s has arrived. " + + "Check for 'red' locks (owned by others) on your Hazard grid and resolve them. " + + "If hazards are separated into temporary grids, please run MergeHazards. " + + "Next...save Hazards grid. Finally, select PlotSPCWatches from the Hazards menu."; + + private static final Map phenTextMap; + + static { + Map phenTextMapTemp = new HashMap(2, 1f); + phenTextMapTemp.put("TO", "Tornado Watch"); + phenTextMapTemp.put("SV", "Severe Thunderstorm Watch"); + phenTextMap = Collections.unmodifiableMap(phenTextMapTemp); + } + + public SPCWatchSrv() { + super(SPC_WATCH_TYPE, SPC_SUPPORTED_PIL); + } + + /* + * (non-Javadoc) + * + * @see com.raytheon.edex.plugin.gfe.warning.AbstractWarningNotifierSrv# + * buildNotification(java.util.List, java.lang.String, + * com.raytheon.uf.common.activetable.VTECPartners) + */ + @Override + protected String buildNotification(List decodedVTEC, + VTECPartners partnersConfig) { + Collection spcSites = partnersConfig + .getSpcSites(DEFAULT_SPC_SITE); + + // find the first record from our configured list of issuing sites. + // Also this product must be a NEW SV.A or TO.A + AbstractWarningRecord matchRecord = null; + for (AbstractWarningRecord e : decodedVTEC) { + if (spcSites.contains(e.getOfficeid()) + && e.getSig().equals("A") + && ((e.getPhen().equals("TO")) || (e.getPhen().equals("SV"))) + && e.getAct().equals("NEW")) { + matchRecord = e; + break; + } + } + + if (matchRecord == null) { + statusHandler.debug("SPC notification: " + + "no SV.A, TO.A vtec lines, or not NEW action code"); + return null; + } + + // create the message + String eventType = phenTextMap.get(matchRecord.getPhen()); + StringBuilder msg = new StringBuilder(String.format(ALERT_MSG, + eventType, matchRecord.getEtn())); + if (matchRecord.getVtecstr().charAt(1) == 'T') { + msg.append(TEST_TEXT_MSG); + } + return msg.toString(); + } +} diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/TPCWatchSrv.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/TPCWatchSrv.java new file mode 100644 index 0000000000..f674ada433 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/TPCWatchSrv.java @@ -0,0 +1,151 @@ +/** + * 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. + **/ +package com.raytheon.edex.plugin.gfe.watch; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import com.raytheon.uf.common.activetable.VTECPartners; +import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord; + +/** + * Watches ingested warnings for WOU products from the SPC (Storm Prediction + * Center). If the warning is a WOU, then it looks to see if the site is in the + * ATTN...WFO... line, and if so, sends a user message to GFE to alert users. + * + *
+ * SOFTWARE HISTORY
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Oct 03, 2008            njensen      Initial creation
+ * Jul 10, 2009  #2590     njensen      Added multiple site support
+ * May 12, 2014  #3157     dgilling     Re-factor based on AbstractWatchNotifierSrv.
+ * 
+ * + * @author njensen + * @version 1.0 + */ + +public final class TPCWatchSrv extends AbstractWatchNotifierSrv { + + private static final String TPC_WATCH_TYPE = "TPC"; + + private static final String TPC_SUPPORTED_PIL = "TCV"; + + private static final String DEFAULT_TPC_SITE = "KNHC"; + + private static final String ALERT_TXT = "Alert: %s has arrived from TPC. " + + "Check for 'red' locks (owned by others) on your Hazard grid and resolve them. " + + "If hazards are separated into temporary grids, please run Mergehazards. " + + "Next...save Hazards grid. Finally, select PlotTPCEvents from Hazards menu."; + + private static final Map phensigMap; + + private static final Map actMap; + + static { + Map phensigMapTemp = new HashMap(5, 1f); + phensigMapTemp.put("HU.A", "Hurricane Watch"); + phensigMapTemp.put("HU.S", "Hurricane Local Statement"); + phensigMapTemp.put("HU.W", "Hurricane Warning"); + phensigMapTemp.put("TR.A", "Tropical Storm Watch"); + phensigMapTemp.put("TR.W", "Tropical Storm Warning"); + phensigMap = Collections.unmodifiableMap(phensigMapTemp); + + Map actMapTemp = new HashMap(3, 1f); + actMapTemp.put("CON", "Continued"); + actMapTemp.put("CAN", "Cancelled"); + actMapTemp.put("NEW", "New"); + actMap = Collections.unmodifiableMap(actMapTemp); + } + + public TPCWatchSrv() { + super(TPC_WATCH_TYPE, TPC_SUPPORTED_PIL); + } + + /* + * (non-Javadoc) + * + * @see com.raytheon.edex.plugin.gfe.warning.AbstractWarningNotifierSrv# + * buildNotification(java.util.List, + * com.raytheon.uf.common.activetable.VTECPartners) + */ + @Override + protected String buildNotification(List decodedVTEC, + VTECPartners partnersConfig) { + Collection tpcSites = partnersConfig + .getTpcSites(DEFAULT_TPC_SITE); + + // get all VTEC records, assemble unique list of phen/sig and storm# + Map> phensigStormAct = new HashMap>(); + for (AbstractWarningRecord e : decodedVTEC) { + if (tpcSites.contains(e.getOfficeid())) { + String phensig = e.getPhensig(); + String storm = e.getEtn(); + String act = e.getAct(); + Set psActs = phensigStormAct.get(phensig + ":" + storm); + if (psActs == null) { + psActs = new TreeSet(); + phensigStormAct.put(phensig + ":" + storm, psActs); + } + psActs.add(act); + } + } + + if (phensigStormAct.isEmpty()) { + statusHandler + .debug("TPC Notification: no HU/TR vtec lines, or not NEW action code"); + return null; + } + + // create the message + StringBuilder msg = new StringBuilder(String.format(ALERT_TXT, + supportedPIL)); + for (String phensigStorm : phensigStormAct.keySet()) { + Collection acts = phensigStormAct.get(phensigStorm); + String[] splitKey = phensigStorm.split(":"); + String phensig = splitKey[0]; + String storm = splitKey[1]; + + String t1 = phensigMap.get(phensig); + if (t1 == null) { + t1 = phensig; + } + msg.append(t1 + ": #" + storm + "("); + String sep = ""; + for (String a : acts) { + String a1 = actMap.get(a); + if (a1 == null) { + a1 = a; + } + msg.append(sep).append(a1); + sep = ","; + } + msg.append("). "); + } + + return msg.toString(); + } +} diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/wcl/WCLWatchSrv.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/WCLWatchSrv.java similarity index 65% rename from edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/wcl/WCLWatchSrv.java rename to edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/WCLWatchSrv.java index 21f1884f0b..a00529d96a 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/wcl/WCLWatchSrv.java +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/WCLWatchSrv.java @@ -20,51 +20,66 @@ /** * */ -package com.raytheon.edex.plugin.gfe.wcl; +package com.raytheon.edex.plugin.gfe.watch; +import java.io.BufferedWriter; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; -import java.io.PrintStream; -import java.nio.channels.FileChannel; +import java.io.Writer; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; import java.util.Date; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.raytheon.edex.plugin.gfe.config.GFESiteActivation; +import com.raytheon.edex.plugin.gfe.server.IFPServer; import com.raytheon.edex.plugin.gfe.util.SendNotifications; +import com.raytheon.uf.common.dataplugin.gfe.server.notify.GfeNotification; import com.raytheon.uf.common.dataplugin.gfe.server.notify.UserMessageNotification; import com.raytheon.uf.common.localization.IPathManager; import com.raytheon.uf.common.localization.LocalizationContext; import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel; import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType; import com.raytheon.uf.common.localization.PathManagerFactory; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.common.time.SimulatedTime; +import com.raytheon.uf.common.util.CollectionUtil; +import com.raytheon.uf.common.util.FileUtil; import com.raytheon.uf.edex.core.EdexException; /** - * @author wldougher + * If a WCL (watch county list) is ingested, this class will send a notification + * to the GFE users alerting them that their WFO may be in the path of an + * upcoming TO.A or SV.A. * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * ??? ??, 20??            wldougher    Initial creation
+ * May 14, 2014  #3157     dgilling     Ensure code works in multi-domain scenarios,
+ *                                      code cleanup.
+ * 
+ * 
+ * + * @author wldougher + * @version 1.0 */ -public class WCLWatchSrv { +public final class WCLWatchSrv { private static final String ALERT_FORM = "Alert: " + "%1$s has arrived. " + "Please select ViewWCL and use %1$s. (Hazards menu)"; - private static final Pattern ATTN_PATTERN = Pattern.compile("^" - + Pattern.quote("ATTN...WFO...")); - private static final Pattern EXPIRE_TIME_PATTERN = Pattern .compile("(\\d{2})(\\d{2})(\\d{2})\\-"); @@ -81,36 +96,8 @@ public class WCLWatchSrv { private static final Pattern UGC_PATTERN = Pattern.compile("\\d{3}\\-"); - protected transient Log logger = LogFactory.getLog(getClass()); - - /** - * Get the WFOs from the ATTN line. - * - * @param lines - * The lines in the warning file - * @return the WFOs from the WFO attention line, as a set of Strings. - */ - protected Set attnWFOs(List lines) { - StringBuilder wfoLine = new StringBuilder(); - boolean attnFound = false; - if (lines != null) { - for (String line : lines) { - attnFound = attnFound || ATTN_PATTERN.matcher(line).lookingAt(); - if (attnFound) { - wfoLine.append(line); - } - } - } - Set wfosR = new HashSet(); - if (wfoLine.length() > 13) { - String[] wfos = wfoLine.substring(13).split(Pattern.quote("...")); - for (String wfo : wfos) { - wfosR.add(wfo.trim()); - } - } - - return wfosR; - } + private static final IUFStatusHandler statusHandler = UFStatus + .getHandler(WCLWatchSrv.class); /** * Process a WCL watch, partially parsed and passed as a WclInfo object. @@ -130,20 +117,23 @@ public class WCLWatchSrv { * or when there are problems generating the WCL script file. */ public void handleWclWatch(WclInfo wclInfo) throws EdexException { - logger.debug("handleWclWatch started"); - UserMessageNotification notice = null; + statusHandler.debug("handleWclWatch started"); + List notifications = Collections.emptyList(); String completeProductPil = wclInfo.getCompleteProductPil(); - Set wfos = attnWFOs(wclInfo.getLines()); + Collection wfos = WatchProductUtil.findAttnWFOs(wclInfo + .getLines()); Set siteIDs = getSiteIDs(); wfos.retainAll(siteIDs); // Keep shared IDs if (!wfos.isEmpty()) { - // Get the first matching site ID - String siteID = wfos.toArray(new String[1])[0]; - + notifications = new ArrayList(wfos.size()); String msg = String.format(ALERT_FORM, completeProductPil); - notice = new UserMessageNotification(msg, Priority.CRITICAL, "GFE", - siteID); + + for (String siteID : wfos) { + GfeNotification notice = new UserMessageNotification(msg, + Priority.CRITICAL, "GFE", siteID); + notifications.add(notice); + } } // Process the WCL regardless of whether we are sending a notice @@ -163,22 +153,19 @@ public class WCLWatchSrv { // Create a dummy Procedure for export String wclStr = makeWclStr(finalUGCList, expireTime, issueTime, watchType); - logger.debug("WCLData: " + wclStr); + statusHandler.debug("WCLData: " + wclStr); - // Write dummy procedure to temp file - File tmpFile = createTempWclFile(wclStr); + // write the WCL file to / + makePermanent(wclStr, completeProductPil); - // Move the file to the wcl folder - // Rename it to / - makePermanent(tmpFile, completeProductPil); - - if (notice == null || !wclInfo.getNotify()) { - logger.info("Notification of WCL skipped"); + if ((wclInfo.getNotify()) + && (!CollectionUtil.isNullOrEmpty(notifications))) { + SendNotifications.send(notifications); } else { - SendNotifications.send(notice); + statusHandler.info("Notification of WCL skipped"); } - logger.debug("handleWclWatch() ending"); + statusHandler.debug("handleWclWatch() ending"); return; } @@ -188,96 +175,46 @@ public class WCLWatchSrv { * that method returns a boolean success flag rather than throwing an error, * so all we can do is tell the user that the rename failed, not why. * - * @param tmpFile - * The temporary file (may be null) + * @param wclData + * WCL data to write to file. * @param completeProductPil * The simple name of the file. - * @throws EdexException - * if tmpFile cannot be renamed. - */ - protected void makePermanent(File tmpFile, String completeProductPil) - throws EdexException { - logger.debug("makePermanent(" + tmpFile + "," + completeProductPil - + ") started"); - if (tmpFile != null) { - File wclDir = getWclDir(); - File dest = new File(wclDir, completeProductPil); - // Try to do things with renameTo() because it's quick if it works. - if (!tmpFile.renameTo(dest)) { - // renameTo() can fail for a variety of reasons. - // Try to do a copy-and-delete. - FileChannel temp = null; - FileChannel perm = null; - IOException firstFail = null; - try { - temp = new FileInputStream(tmpFile).getChannel(); - perm = new FileOutputStream(dest).getChannel(); - // should file range be locked before copy? - temp.transferTo(0, temp.size(), perm); - temp.close(); - tmpFile.delete(); - } catch (IOException e) { - throw new EdexException("Renaming \"" - + tmpFile.getAbsolutePath() + "\" to \"" - + dest.getAbsolutePath() + "\" failed.", e); - } finally { - if (temp != null && temp.isOpen()) { - try { - temp.close(); - logger.debug(temp.toString() + " closed"); - } catch (IOException e) { - firstFail = e; - } - } - if (perm != null && perm.isOpen()) { - try { - perm.close(); - logger.debug(perm.toString() + " closed"); - } catch (IOException e) { - if (firstFail == null) { - firstFail = e; - } - } - } - } - if (firstFail != null) { - throw new EdexException("Error closing file", firstFail); - } - } - // If we got to here, claim success! - logger.info("" + tmpFile.getAbsolutePath() + " renamed to " - + dest.getAbsolutePath()); - } - logger.debug("makePermanent(" + tmpFile + "," + completeProductPil - + ") ending"); - } - - /** - * Create a temporary file with the prefix "wcl" and the default suffix in - * the default temporary file directory. Write all of wclStr into it. * - * @param wclStr - * the String containing the contents to write to the file - * @return the File created. * @throws EdexException - * if the file cannot be written + * if WCL file cannot be opened, written, or closed. */ - protected File createTempWclFile(String wclStr) throws EdexException { - File tmpFile = null; - PrintStream wclOut = null; + protected void makePermanent(String wclData, String completeProductPil) + throws EdexException { + statusHandler.debug("makePermanent for [" + completeProductPil + + "] started"); + + File wclDir = getWclDir(); + File dest = new File(wclDir, completeProductPil); + + Writer output = null; try { - tmpFile = File.createTempFile("wcl", null, null); - wclOut = new PrintStream(tmpFile); - wclOut.println(wclStr); + output = new BufferedWriter(new FileWriter(dest)); + output.write(wclData); + output.write("\n"); + + // If we got to here, claim success! + statusHandler.info("Wrote new WCL to " + dest.getAbsolutePath()); } catch (IOException e) { - throw new EdexException("Error writing parsed WCL to file \"" - + tmpFile.getAbsolutePath() + "\"", e); + throw new EdexException("Could not write new WCL file " + + dest.getAbsolutePath(), e); } finally { - if (wclOut != null) { - wclOut.close(); + if (output != null) { + try { + output.close(); + } catch (IOException e) { + throw new EdexException("Could not close new WCL file " + + dest.getAbsolutePath(), e); + } } + } - return tmpFile; + statusHandler.debug("makePermanent for [" + completeProductPil + + "] ending"); } /** @@ -436,7 +373,7 @@ public class WCLWatchSrv { * @return a Set of Strings representing the site IDs. */ protected Set getSiteIDs() { - Set siteIDs = GFESiteActivation.getInstance().getActiveSites(); + Set siteIDs = IFPServer.getActiveSites(); return siteIDs; } @@ -451,13 +388,14 @@ public class WCLWatchSrv { IPathManager pathManager = PathManagerFactory.getPathManager(); LocalizationContext ctx = pathManager.getContext( LocalizationType.CAVE_STATIC, LocalizationLevel.SITE); - String wclName = "gfe" + File.separator + "wcl"; + String wclName = FileUtil.join("gfe", "wcl"); File wclDir = pathManager.getFile(ctx, wclName); if (wclDir == null) { - logger.error("Path manager could not locate " + wclName); + statusHandler.error("Path manager could not locate " + wclName); } else if (!wclDir.exists()) { wclDir.mkdir(); - logger.info("Directory " + wclDir.getAbsolutePath() + " created."); + statusHandler.info("Directory " + wclDir.getAbsolutePath() + + " created."); } return wclDir; } diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/WatchProductUtil.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/WatchProductUtil.java new file mode 100644 index 0000000000..53d209f78a --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/WatchProductUtil.java @@ -0,0 +1,96 @@ +/** + * 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. + **/ +package com.raytheon.edex.plugin.gfe.watch; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.raytheon.uf.common.util.StringUtil; + +/** + * Common methods for dealing with watch products that are received from + * national centers. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * May 14, 2014  #3157     dgilling     Initial creation.
+ * 
+ * 
+ * + * @author dgilling + * @version 1.0 + */ + +public class WatchProductUtil { + + private static final Pattern ATTN_PATTERN = Pattern + .compile("\\QATTN...WFO...\\E((?:[A-Z]{3}\\Q...\\E)+)"); + + private WatchProductUtil() { + throw new AssertionError(); + } + + /** + * Searches the specified watch product text for a "ATTN...WFO..." line and + * returns a collection of WFOs that appeared in that line of the product. + * + * @param lines + * The lines that comprise the watch product. + * @return The list of WFOs that appear in the "ATTN...WFO..." line of the + * product. + */ + public static Collection findAttnWFOs(List lines) { + return findAttnWFOs(StringUtil.join(lines, '\n')); + } + + /** + * Searches the specified watch product text for a "ATTN...WFO..." line and + * returns a collection of WFOs that appeared in that line of the product. + * + * @param rawMessage + * The full text of the watch product in a single String. + * @returnThe list of WFOs that appear in the "ATTN...WFO..." line of the + * product. + */ + public static Collection findAttnWFOs(String rawMessage) { + Collection retVal = Collections.emptySet(); + + // decode the ATTN line, which tells us which WFOs are affected + // only used for WCL and WOU products + Matcher m = WatchProductUtil.ATTN_PATTERN.matcher(rawMessage); + if (m.find()) { + // eliminate ATTN...WFO... + String found = m.group(1); + String[] wfos = found.split(Pattern.quote("...")); + retVal = new HashSet(Arrays.asList(wfos)); + } + + return retVal; + } +} diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/wcl/WclInfo.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/WclInfo.java similarity index 98% rename from edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/wcl/WclInfo.java rename to edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/WclInfo.java index a934d9cfaa..b0b91a2e00 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/wcl/WclInfo.java +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/watch/WclInfo.java @@ -20,7 +20,7 @@ /** * */ -package com.raytheon.edex.plugin.gfe.wcl; +package com.raytheon.edex.plugin.gfe.watch; import java.util.Collections; import java.util.Date; diff --git a/edexOsgi/com.raytheon.edex.plugin.warning/WarningDecoder.py b/edexOsgi/com.raytheon.edex.plugin.warning/WarningDecoder.py index bca33862cd..0e30f99592 100644 --- a/edexOsgi/com.raytheon.edex.plugin.warning/WarningDecoder.py +++ b/edexOsgi/com.raytheon.edex.plugin.warning/WarningDecoder.py @@ -42,6 +42,7 @@ # start time from file's timestamp. # Oct 03, 2013 2402 bsteffen Make PythonDecoder more extendable. # May 15, 2014 2536 bclement moved WMO time parsing to WMOTimeParser +# May 15, 2014 3157 dgilling Update location of WclInfo class. # # @@ -176,7 +177,7 @@ class StdWarningDecoder(): if self._productPil[0:3] == "WCL": endpoint = "WCLWatch" # build a Java object for the warning - from com.raytheon.edex.plugin.gfe.wcl import WclInfo + from com.raytheon.edex.plugin.gfe.watch import WclInfo import JUtil lines = JUtil.pyValToJavaObj(self._lines) warning = WclInfo(long(self._issueTime * 1000), diff --git a/edexOsgi/com.raytheon.uf.common.activetable/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.activetable/META-INF/MANIFEST.MF index b23ac00488..2a35e519ce 100644 --- a/edexOsgi/com.raytheon.uf.common.activetable/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.common.activetable/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Activetable Plug-in Bundle-SymbolicName: com.raytheon.uf.common.activetable -Bundle-Version: 1.12.1174.qualifier +Bundle-Version: 1.14.0.qualifier Bundle-Vendor: RAYTHEON Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Eclipse-RegisterBuddy: com.raytheon.uf.common.serialization @@ -12,8 +12,7 @@ Import-Package: com.raytheon.uf.common.dataplugin.annotations, com.raytheon.uf.common.serialization.comm, com.vividsolutions.jts.geom, javax.persistence, - org.hibernate.annotations, - org.springframework.beans.factory.annotation + org.hibernate.annotations Export-Package: com.raytheon.uf.common.activetable, com.raytheon.uf.common.activetable.request, com.raytheon.uf.common.activetable.response @@ -22,4 +21,5 @@ Require-Bundle: com.raytheon.uf.common.serialization, com.raytheon.uf.common.localization;bundle-version="1.12.1174", com.raytheon.uf.common.python;bundle-version="1.12.1174", org.jep;bundle-version="1.0.0", - com.raytheon.uf.common.util;bundle-version="1.12.1174" + com.raytheon.uf.common.util;bundle-version="1.12.1174", + com.raytheon.uf.common.status diff --git a/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/VTECPartners.java b/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/VTECPartners.java index 5585d38eb8..4898654fa2 100644 --- a/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/VTECPartners.java +++ b/edexOsgi/com.raytheon.uf.common.activetable/src/com/raytheon/uf/common/activetable/VTECPartners.java @@ -21,7 +21,11 @@ package com.raytheon.uf.common.activetable; import java.io.File; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; @@ -34,6 +38,8 @@ import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType; import com.raytheon.uf.common.localization.PathManagerFactory; import com.raytheon.uf.common.python.PyUtil; import com.raytheon.uf.common.python.PythonScript; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; /** * This wraps the VTECPartners.py file, loading it as a Map in @@ -46,7 +52,9 @@ import com.raytheon.uf.common.python.PythonScript; * SOFTWARE HISTORY * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Jul 8, 2010 wldougher Initial creation + * Jul 08, 2010 wldougher Initial creation + * May 15, 2010 #3157 dgilling Add convenience methods for retrieving + * TPC and SPC sites. * * * @@ -58,6 +66,9 @@ public class VTECPartners { private static final String CONFIG_PATH = "config" + File.separator + "gfe"; + private static final transient IUFStatusHandler statusHandler = UFStatus + .getHandler(VTECPartners.class); + private static Map instanceMap; private Map simpleObjects; @@ -187,4 +198,49 @@ public class VTECPartners { } return obj; } + + public Collection getSpcSites() { + return getSpcSites(null); + } + + public Collection getSpcSites(String defaultSite) { + return getSupportedIssuingSites("VTEC_SPC_SITE", defaultSite); + } + + public Collection getTpcSites() { + return getTpcSites(null); + } + + public Collection getTpcSites(String defaultSite) { + return getSupportedIssuingSites("VTEC_TPC_SITE", defaultSite); + } + + protected Collection getSupportedIssuingSites(String settingName, + String defaultValue) { + Object pyObject = getattr(settingName, defaultValue); + + if (pyObject == null) { + String msg = String + .format("VTECPartners setting [%s] not configured. Check your localVTECPartners settings.", + settingName); + statusHandler.warn(msg); + } else if (pyObject instanceof String) { + String singleSite = (String) pyObject; + return new HashSet(Arrays.asList(singleSite)); + } else if (pyObject instanceof Collection) { + Collection siteList = (Collection) pyObject; + Collection retVal = new HashSet(siteList.size(), 1f); + for (Object siteObj : siteList) { + retVal.add(siteObj.toString()); + } + return retVal; + } else { + String msg = String + .format("Unsupported value for setting [%s]: %s. Check your localVTECPartners settings.", + pyObject.toString(), settingName); + statusHandler.warn(msg); + } + + return Collections.emptySet(); + } } diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/ActiveTable.java b/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/ActiveTable.java index 674a69cffd..cb6b4e0072 100644 --- a/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/ActiveTable.java +++ b/edexOsgi/com.raytheon.uf.edex.activetable/src/com/raytheon/uf/edex/activetable/ActiveTable.java @@ -83,6 +83,8 @@ import com.raytheon.uf.edex.database.query.DatabaseQuery; * Mar 06, 2014 2883 randerso Pass siteId into python code * Apr 10, 2014 3004 dgilling Remove ActiveTableMode parameter from * clearPracticeTable(). + * May 15, 2014 3157 dgilling Add support for multiple TPC and SPC + * issuing sites. * * * @@ -199,12 +201,8 @@ public class ActiveTable { List wfoList = (List) vtecPartners .getattr("VTEC_DECODER_SITES"); wfoSet.addAll(wfoList); - String spcSite = (String) vtecPartners - .getattr("VTEC_SPC_SITE"); - wfoSet.add(spcSite); - String tpcSite = (String) vtecPartners - .getattr("VTEC_TPC_SITE"); - wfoSet.add(tpcSite); + wfoSet.addAll(vtecPartners.getSpcSites()); + wfoSet.addAll(vtecPartners.getTpcSites()); } wfoSet.add(siteId); wfos = wfoSet.toArray(new String[0]); diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/ActiveTable.py b/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/ActiveTable.py index f7c35bc5f9..916cdba586 100644 --- a/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/ActiveTable.py +++ b/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/ActiveTable.py @@ -28,6 +28,7 @@ # 06/11/13 #2083 randerso Log active table changes, save backups # 03/06/14 #2883 randerso Pass siteId into mergeFromJava # 03/25/14 #2884 randerso Added xxxid to VTECChange +# 05/15/14 #3157 dgilling Support multiple TPC and SPC sites. # import time @@ -35,10 +36,12 @@ import copy import os import VTECTableUtil, VTECTableSqueeze, VTECPartners import LogStream, ActiveTableVtec, ActiveTableRecord +import JUtil from java.util import ArrayList from com.raytheon.uf.common.localization import PathManagerFactory from com.raytheon.uf.common.localization import LocalizationContext_LocalizationType as LocalizationType from com.raytheon.uf.common.localization import LocalizationContext_LocalizationLevel as LocalizationLevel +from com.raytheon.uf.common.activetable import VTECPartners as JavaVTECPartners class ActiveTable(VTECTableUtil.VTECTableUtil): @@ -257,8 +260,10 @@ def mergeFromJava(siteId, activeTable, newRecords, logger, mode, offsetSecs=0): decoderSites = VTECPartners.VTEC_DECODER_SITES decoderSites.append(VTECPartners.get4ID(siteId)) - decoderSites.append(VTECPartners.VTEC_SPC_SITE) - decoderSites.append(VTECPartners.VTEC_TPC_SITE) + spcSites = JUtil.javaObjToPyVal(JavaVTECPartners.getInstance(siteId).getSpcSites()) + decoderSites.extend(spcSites) + tpcSites = JUtil.javaObjToPyVal(JavaVTECPartners.getInstance(siteId).getTpcSites()) + decoderSites.extend(tpcSites) backup = False pyNew = [] diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/MergeVTEC.py b/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/MergeVTEC.py index 1eb9d6a397..4e2025cbf6 100644 --- a/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/MergeVTEC.py +++ b/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/MergeVTEC.py @@ -34,6 +34,7 @@ # 06/11/13 #2083 randerso Move backups to edex_static # 01/24/14 #2504 randerso change to use iscUtil.getLogger for consistency # 03/25/14 #2884 randerso Added xxxid to VTECChange +# 05/15/14 #3157 dgilling Support multiple TPC and SPC sites. # @@ -49,6 +50,7 @@ import siteConfig import VTECPartners import VTECTableSqueeze import VTECTableUtil +import JUtil from java.util import ArrayList from com.raytheon.uf.common.activetable import MergeResult @@ -56,6 +58,7 @@ from com.raytheon.uf.common.activetable import VTECChange from com.raytheon.uf.common.localization import PathManagerFactory from com.raytheon.uf.common.localization import LocalizationContext_LocalizationType as LocalizationType from com.raytheon.uf.common.site import SiteMap +from com.raytheon.uf.common.activetable import VTECPartners as JavaVTECPartners class MergeVTEC(VTECTableUtil.VTECTableUtil): @@ -90,8 +93,8 @@ class MergeVTEC(VTECTableUtil.VTECTableUtil): VTECTableUtil.VTECTableUtil.__init__(self, fileName) # get the SPC site id from the configuration file - self._spcSite = getattr(VTECPartners, "VTEC_SPC_SITE", "KWNS") - self._tpcSite = getattr(VTECPartners, "VTEC_TPC_SITE", "KNHC") + self._spcSite = JUtil.javaObjToPyVal(JavaVTECPartners.getInstance(siteConfig.GFESUITE_SITEID).getSpcSites("KWNS")) + self._tpcSite = JUtil.javaObjToPyVal(JavaVTECPartners.getInstance(siteConfig.GFESUITE_SITEID).getTpcSites("KNHC")) # get our site siteid = siteConfig.GFESUITE_SITEID @@ -393,8 +396,8 @@ class MergeVTEC(VTECTableUtil.VTECTableUtil): sites = getattr(VTECPartners, "VTEC_MERGE_SITES", []) if sites is None: return None - sites.append(self._spcSite) - sites.append(self._tpcSite) + sites.extend(self._spcSite) + sites.extend(self._tpcSite) sites.append(self._ourSite) self._log.debug("Filter Sites: %s", sites) return sites diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/VTECPartners.py b/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/VTECPartners.py index 5011ab9aac..3409535112 100644 --- a/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/VTECPartners.py +++ b/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/VTECPartners.py @@ -37,8 +37,8 @@ VTEC_TABLE_REQUEST_SITES = [] # Name of site identifier for SPC and TCV bulletins. 4-characters. -VTEC_SPC_SITE = 'KWNS' -VTEC_TPC_SITE = 'KNHC' +VTEC_SPC_SITE = ['KWNS'] +VTEC_TPC_SITE = ['KNHC'] # The following list is a set of office identifiers which is used # in the ingestAT/MergeVTEC software to filter out records from offices diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/requestAT.py b/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/requestAT.py index 460a08bb94..b45d4ef653 100644 --- a/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/requestAT.py +++ b/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/requestAT.py @@ -27,7 +27,8 @@ # Date Ticket# Engineer Description # ------------ ---------- ----------- -------------------------- # 02/06/13 1447 dgilling Initial Creation. -# 01/24/14 2504 randerso change to use iscUtil.getLogger for consistency +# 01/24/14 2504 randerso change to use iscUtil.getLogger for consistency +# 05/15/14 #3157 dgilling Support multiple TPC and SPC sites. # # @@ -42,10 +43,12 @@ import IrtAccess import siteConfig import VTECPartners import iscUtil +import JUtil from com.raytheon.uf.common.activetable import ActiveTableMode from com.raytheon.uf.common.time.util import TimeUtil from com.raytheon.uf.edex.activetable import ActiveTable +from com.raytheon.uf.common.activetable import VTECPartners as JavaVTECPartners @@ -73,8 +76,11 @@ def execute_request_at(serverHost, serverPort, serverProtocol, mhsid, siteID, an mysite4 = "PAFC" else: mysite4 = "K" + siteID - otherSites = [mysite4, VTECPartners.VTEC_SPC_SITE, - VTECPartners.VTEC_TPC_SITE] + otherSites = [mysite4] + tpcSites = JUtil.javaObjToPyVal(JavaVTECPartners.getInstance(siteID).getTpcSites()) + spcSites = JUtil.javaObjToPyVal(JavaVTECPartners.getInstance(siteID).getSpcSites()) + otherSites.extend(tpcSites) + otherSites.extend(spcSites) # determine the MHS WMO id for this message wmoid = "TTAA00 " + mysite4 diff --git a/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/sendAT.py b/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/sendAT.py index b7a9da8ac0..8d144db064 100644 --- a/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/sendAT.py +++ b/edexOsgi/com.raytheon.uf.edex.activetable/utility/common_static/base/vtec/sendAT.py @@ -27,7 +27,8 @@ # Date Ticket# Engineer Description # ------------ ---------- ----------- -------------------------- # 02/08/13 1447 dgilling Initial Creation. -# 01/24/14 2504 randerso change to use iscUtil.getLogger for consistency +# 01/24/14 2504 randerso change to use iscUtil.getLogger for consistency +# 05/15/14 #3157 dgilling Support multiple TPC and SPC sites. # # @@ -48,6 +49,9 @@ import VTECPartners import VTECTableSqueeze import iscUtil +from com.raytheon.uf.common.activetable import VTECPartners as JavaVTECPartners + + # Configuration Item for Test Purposes FORCE_SEND = False #Set to True to always send even if no updates required. @@ -88,10 +92,12 @@ def execute_send_at(myServerHost, myServerPort, myServerProtocol, filtTable = [] # filter by sites listing if filterSites: + tpcSites = JUtil.javaObjToPyVal(JavaVTECPartners.getInstance(myServerSite).getTpcSites()) + spcSites = JUtil.javaObjToPyVal(JavaVTECPartners.getInstance(myServerSite).getSpcSites()) + for t in table: if t['oid'] in filterSites or t['oid'][1:4] in sites or \ - t['oid'] == VTECPartners.VTEC_SPC_SITE or \ - t['oid'] == VTECPartners.VTEC_TPC_SITE: + t['oid'] in tpcSites or t['oid'] in spcSites: filtTable.append(t) else: filtTable = table #no filtering