Merge "Omaha #3008 Refactor to use EventReport to parse reports and better log reports with missing fields. Update manifest. Log number of discarded reports. Discard all reports if missing the data range." into omaha_14.4.1
Former-commit-id:3b2dc2915e
[formerly35e3626527
] [formerly3b2dc2915e
[formerly35e3626527
] [formerly1d2ae81ff6
[formerly 76fd0a563baa40722ce645d8019c40dbc42405b6]]] Former-commit-id:1d2ae81ff6
Former-commit-id:14d368f33b
[formerly8a03a5ecb9
] Former-commit-id:c0096b0570
This commit is contained in:
commit
5ed2cdc4ec
5 changed files with 687 additions and 269 deletions
|
@ -2,9 +2,9 @@ Manifest-Version: 1.0
|
|||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Svrwx Plug-in
|
||||
Bundle-SymbolicName: com.raytheon.uf.edex.plugin.svrwx
|
||||
Bundle-Version: 1.12.1174.qualifier
|
||||
Bundle-Version: 1.14.0.qualifier
|
||||
Bundle-Vendor: RAYTHEON
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
|
||||
Require-Bundle: com.raytheon.uf.common.dataplugin.svrwx;bundle-version="1.0.0",
|
||||
com.raytheon.uf.common.pointdata,
|
||||
com.raytheon.uf.edex.pointdata,
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
/**
|
||||
* 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.uf.edex.plugin.svrwx.decoder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* An InternalReport that contains an entire event report. The fields of a
|
||||
* report are parsed from the data, but not transformed in any way.
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jun 25, 2014 3008 nabowle Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author nabowle
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
public class EventReport extends InternalReport {
|
||||
|
||||
private static final Pattern EVENT_KEY_PTRN = Pattern.compile(EVENT_KEY);
|
||||
|
||||
private static final Pattern TIME_PTRN = Pattern.compile(TIME);
|
||||
|
||||
private static final Pattern STATIONID_PTRN = Pattern.compile(STATIONID);
|
||||
|
||||
private static final Pattern LATLON_PTRN = Pattern.compile(LATLON);
|
||||
|
||||
private String event;
|
||||
|
||||
private String time;
|
||||
|
||||
private String key;
|
||||
|
||||
private String remarks;
|
||||
|
||||
private String latLon;
|
||||
|
||||
private String stationId;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param event
|
||||
* The event line.
|
||||
* @param pKey
|
||||
* The event key.
|
||||
* @param pTime
|
||||
* The event time.
|
||||
* @param station
|
||||
* The station id.
|
||||
* @param latlon
|
||||
* The latitude/longitude.
|
||||
* @param rmks
|
||||
* The remarks.
|
||||
*/
|
||||
private EventReport(String event, String pKey, String pTime,
|
||||
String station, String latlon, String rmks) {
|
||||
super(InternalType.EVENT_REPORT, event);
|
||||
this.key = pKey;
|
||||
this.time = pTime;
|
||||
this.stationId = station;
|
||||
this.latLon = latlon;
|
||||
this.remarks = rmks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the event
|
||||
*/
|
||||
public String getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the time
|
||||
*/
|
||||
public String getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the key
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the remarks
|
||||
*/
|
||||
public String getRemarks() {
|
||||
return remarks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the latitude
|
||||
*/
|
||||
public String getLatLon() {
|
||||
return latLon;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the stationId
|
||||
*/
|
||||
public String getStationId() {
|
||||
return stationId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for the EventReport to facilitate creating an EventReport from
|
||||
* related data lines.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private String eventLine;
|
||||
|
||||
private List<String> remarks = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public Builder() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an EventReport object from the event line and remarks.
|
||||
*
|
||||
* @return The built EventReport.
|
||||
*/
|
||||
public EventReport build() {
|
||||
return new EventReport(buildEventString(), parseEventKey(),
|
||||
parseTime(), parseStationId(), parseLatLon(),
|
||||
parseRemarks());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event line of the report.
|
||||
*
|
||||
* @param eventLine
|
||||
* The event line.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder withEventLine(String eventLine) {
|
||||
this.eventLine = eventLine;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a remarks line to the report.
|
||||
*
|
||||
* @param rmks
|
||||
* The remarks line.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder withRemarks(String rmks) {
|
||||
this.remarks.add(rmks);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a String of the entire event, including the event line and all
|
||||
* remarks lines.
|
||||
*
|
||||
* @return The event string.
|
||||
*/
|
||||
private String buildEventString() {
|
||||
StringBuilder sb = new StringBuilder(this.eventLine);
|
||||
for (String rmk : this.remarks) {
|
||||
sb.append("\n").append(rmk);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the event key field.
|
||||
*
|
||||
* @return The event key field.
|
||||
*/
|
||||
private String parseEventKey() {
|
||||
return parse(this.eventLine, EVENT_KEY_PTRN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the time field.
|
||||
*
|
||||
* @return the time field.
|
||||
*/
|
||||
private String parseTime() {
|
||||
return parse(this.eventLine, TIME_PTRN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the station id field.
|
||||
*
|
||||
* @return The station id field.
|
||||
*/
|
||||
private String parseStationId() {
|
||||
String station = parseFromRemarks(STATIONID_PTRN);
|
||||
|
||||
if (station != null) {
|
||||
station = station.substring(0, 3);
|
||||
}
|
||||
|
||||
return station;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the combined latitude/longitude field.
|
||||
*
|
||||
* @return The combined latitude/longitude field.
|
||||
*/
|
||||
private String parseLatLon() {
|
||||
return parseFromRemarks(LATLON_PTRN);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parses the remarks field from the remarks lines. The first remarks
|
||||
* line contains other fields, starting with the station id, which are
|
||||
* not included in the remarks field.
|
||||
*
|
||||
* @return The parsed remarks fields.
|
||||
*/
|
||||
private String parseRemarks() {
|
||||
String ret;
|
||||
if (this.remarks.isEmpty()) {
|
||||
ret = null;
|
||||
} else {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String remark = this.remarks.get(0);
|
||||
Matcher m = STATIONID_PTRN.matcher(remark);
|
||||
|
||||
if (m.find()) {
|
||||
sb.append(remark.substring(0, m.start()).trim());
|
||||
} else {
|
||||
sb.append(remark.trim());
|
||||
}
|
||||
|
||||
for (int i = 1; i < this.remarks.size(); i++) {
|
||||
sb.append(" ").append(this.remarks.get(i).trim());
|
||||
}
|
||||
|
||||
ret = sb.toString();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a field from a line.
|
||||
*
|
||||
* @param line
|
||||
* The line to pull the field from.
|
||||
* @param pattern
|
||||
* The pattern of the field.
|
||||
* @return The matched group, or null if the pattern does not match the
|
||||
* line.
|
||||
*/
|
||||
private String parse(String line, Pattern pattern) {
|
||||
String ret;
|
||||
if (line == null) {
|
||||
ret = null;
|
||||
} else {
|
||||
Matcher m = pattern.matcher(line);
|
||||
if (m.find()) {
|
||||
ret = m.group();
|
||||
} else {
|
||||
ret = null;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a field from the first remarks line.
|
||||
*
|
||||
* @param pattern
|
||||
* The pattern of the field.
|
||||
* @return The matched group, or null if the remarks or empty or the
|
||||
* pattern does not match the remarks line.
|
||||
*/
|
||||
private String parseFromRemarks(Pattern pattern) {
|
||||
String ret;
|
||||
if (this.remarks.isEmpty()) {
|
||||
ret = null;
|
||||
} else {
|
||||
ret = parse(this.remarks.get(0), pattern);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +1,19 @@
|
|||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
@ -33,18 +33,19 @@ import com.raytheon.uf.common.status.UFStatus;
|
|||
|
||||
/**
|
||||
* Internal Report
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jan 10, 2010 jsanchez Initial creation
|
||||
* Apr 10, 2014 2971 skorolev Cleaned code.
|
||||
*
|
||||
* Jun 25, 2014 3008 nabowle Refactor for EventReport.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @author jsanchez
|
||||
* @version 1.0
|
||||
*/
|
||||
|
@ -74,8 +75,6 @@ public class InternalReport {
|
|||
public static final String RMK_LN = "^((.*)" + STATIONID + "(.*)" + LATLON
|
||||
+ ")";
|
||||
|
||||
private static final Pattern RMK_LN_PTRN = Pattern.compile(RMK_LN);
|
||||
|
||||
public static final String REFTIME = "(\\d{2,2}CST\\s\\w{3,3}\\s\\w{3,3}\\s{1,2}\\d{1,2}\\s{1,2}\\d{4,4})";
|
||||
|
||||
public static final String TIME_RANGE_LN = "^((.*)FOR\\s" + REFTIME
|
||||
|
@ -91,7 +90,7 @@ public class InternalReport {
|
|||
private List<InternalReport> subLines = null;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
* @param line
|
||||
*/
|
||||
|
@ -102,7 +101,7 @@ public class InternalReport {
|
|||
|
||||
/**
|
||||
* Get Line Type.
|
||||
*
|
||||
*
|
||||
* @return the lineType
|
||||
*/
|
||||
public InternalType getLineType() {
|
||||
|
@ -111,7 +110,7 @@ public class InternalReport {
|
|||
|
||||
/**
|
||||
* Get Report Line.
|
||||
*
|
||||
*
|
||||
* @return the reportLine
|
||||
*/
|
||||
public String getReportLine() {
|
||||
|
@ -120,7 +119,7 @@ public class InternalReport {
|
|||
|
||||
/**
|
||||
* Get SubLines.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<InternalReport> getSubLines() {
|
||||
|
@ -128,7 +127,7 @@ public class InternalReport {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param buffer
|
||||
* Buffer to receive String formatted internal data. If this
|
||||
* reference is null, a new StringBuilder instance is created.
|
||||
|
@ -148,7 +147,7 @@ public class InternalReport {
|
|||
|
||||
/**
|
||||
* Create a string representation of this class instance.
|
||||
*
|
||||
*
|
||||
* @return The string representation of this class instance.
|
||||
*/
|
||||
@Override
|
||||
|
@ -165,7 +164,7 @@ public class InternalReport {
|
|||
|
||||
/**
|
||||
* Message identification.
|
||||
*
|
||||
*
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
|
@ -173,20 +172,25 @@ public class InternalReport {
|
|||
List<InternalReport> reports = new ArrayList<InternalReport>();
|
||||
List<String> lines = separateLines(message);
|
||||
if (lines != null) {
|
||||
InternalType t1 = InternalType.REPORT_TYPE;
|
||||
InternalType t2 = InternalType.EVENT_LN;
|
||||
InternalType t3 = InternalType.REMARKS;
|
||||
InternalType t4 = InternalType.TIME_RANGE;
|
||||
|
||||
Pattern patterns[] = { REPORT_TYPE_LN_PTRN, EVENT_LN_PTRN,
|
||||
RMK_LN_PTRN, TIME_RANGE_LN_PTRN };
|
||||
InternalType types[] = { t1, t2, t3, t4 };
|
||||
Pattern patterns[] = { REPORT_TYPE_LN_PTRN, TIME_RANGE_LN_PTRN };
|
||||
InternalType types[] = { InternalType.REPORT_TYPE,
|
||||
InternalType.TIME_RANGE };
|
||||
|
||||
boolean found;
|
||||
EventReport.Builder builder = null;
|
||||
Matcher m;
|
||||
for (String s : lines) {
|
||||
found = false;
|
||||
for (int i = 0; i < patterns.length; i++) {
|
||||
Matcher m = patterns[i].matcher(s);
|
||||
m = patterns[i].matcher(s);
|
||||
if (m.matches()) {
|
||||
|
||||
if (builder != null) {
|
||||
reports.add(builder.build());
|
||||
builder = null;
|
||||
}
|
||||
|
||||
InternalReport rptLine = new InternalReport(types[i], s);
|
||||
reports.add(rptLine);
|
||||
found = true;
|
||||
|
@ -194,20 +198,42 @@ public class InternalReport {
|
|||
}
|
||||
}
|
||||
if (!found) {
|
||||
InternalReport rptLine = new InternalReport(
|
||||
InternalType.EXTRA, s);
|
||||
reports.add(rptLine);
|
||||
// TODO: An unrecognized event line will lead to either:
|
||||
// The previous report's remarks containing the report or
|
||||
// the report being tagged as EXTRA lines.
|
||||
//
|
||||
// In the former case, the issue may be noticed if the
|
||||
// remarks of the report are examined. In the latter,
|
||||
// the report will be lost without a trace.
|
||||
// More info:
|
||||
// http://www.nws.noaa.gov/directives/sym/pd01005012curr.pdf
|
||||
// http://www.spc.noaa.gov/misc/about.html#Statistics
|
||||
m = EVENT_LN_PTRN.matcher(s);
|
||||
if (m.matches()) {
|
||||
if (builder != null) {
|
||||
reports.add(builder.build());
|
||||
}
|
||||
builder = new EventReport.Builder().withEventLine(s);
|
||||
} else if (builder != null) {
|
||||
builder.withRemarks(s);
|
||||
} else {
|
||||
InternalReport rptLine = new InternalReport(
|
||||
InternalType.EXTRA, s);
|
||||
reports.add(rptLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
InternalReport rptLine = new InternalReport(InternalType.END, "");
|
||||
reports.add(rptLine);
|
||||
|
||||
if (builder != null) {
|
||||
reports.add(builder.build());
|
||||
}
|
||||
}
|
||||
return reports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Separate Lines.
|
||||
*
|
||||
*
|
||||
* @param message
|
||||
* @return reportLines
|
||||
*/
|
||||
|
@ -222,7 +248,7 @@ public class InternalReport {
|
|||
String s;
|
||||
reportLines = new ArrayList<String>();
|
||||
while ((s = reader.readLine()) != null) {
|
||||
if (s.length() > 0) {
|
||||
if (s.trim().length() > 0) {
|
||||
reportLines.add(s);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
@ -30,6 +30,7 @@ package com.raytheon.uf.edex.plugin.svrwx.decoder;
|
|||
* ------------ ---------- ----------- --------------------------
|
||||
* Jan 05, 2010 jsanchez Initial creation
|
||||
* Apr 10, 2014 skorolev Cleaned code.
|
||||
* Jun 25, 2014 3008 nabowle Removed unused values.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -37,5 +38,5 @@ package com.raytheon.uf.edex.plugin.svrwx.decoder;
|
|||
* @version 1.0
|
||||
*/
|
||||
public enum InternalType {
|
||||
TIME_RANGE, REPORT_TYPE, EVENT_LN, REMARKS, EXTRA, END, WRONG_LN;
|
||||
TIME_RANGE, REPORT_TYPE, EXTRA, EVENT_REPORT;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
@ -21,6 +21,7 @@ package com.raytheon.uf.edex.plugin.svrwx.decoder;
|
|||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -43,9 +44,11 @@ import com.raytheon.uf.edex.plugin.svrwx.SvrWxRecordDao;
|
|||
|
||||
/**
|
||||
* SvrWx Parser
|
||||
*
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
*
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
|
@ -53,9 +56,10 @@ import com.raytheon.uf.edex.plugin.svrwx.SvrWxRecordDao;
|
|||
* Aug 30, 2013 2298 rjpeter Make getPluginName abstract
|
||||
* Apr 07, 2014 2971 skorolev Add condition to avoid malformed parts in the message.
|
||||
* May 14, 2014 2536 bclement moved WMO Header to common, removed pluginName
|
||||
*
|
||||
* Jun 25, 2014 3008 nabowle Refactor for EventReport type
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @author jsanchez
|
||||
* @version 1.0
|
||||
*/
|
||||
|
@ -81,52 +85,19 @@ public class SvrWxParser {
|
|||
|
||||
private List<SvrWxRecord> reports;
|
||||
|
||||
private String eventKey;
|
||||
|
||||
private DataTime refTime;
|
||||
|
||||
private String greenTime;
|
||||
|
||||
private int month;
|
||||
|
||||
private int year;
|
||||
|
||||
private String remarks;
|
||||
|
||||
private Float latitude;
|
||||
|
||||
private Float longitude;
|
||||
|
||||
private String stationId;
|
||||
|
||||
private String reportType;
|
||||
|
||||
public static final String TORN = "*TORN";
|
||||
|
||||
public static final String WNDG = "WNDG";
|
||||
|
||||
private static final HashMap<String, Integer> MONTH_MAP = new HashMap<String, Integer>();
|
||||
|
||||
/** List of lines with non-parsed location. */
|
||||
private List<String> badLines;
|
||||
private static final Pattern YEAR_PTRN = Pattern.compile("\\d{4,4}");
|
||||
|
||||
private static final Pattern EVENT_KEY_PTRN = Pattern
|
||||
.compile(InternalReport.EVENT_KEY);
|
||||
|
||||
private static final Pattern DATE_TIME_PTRN = Pattern
|
||||
.compile(InternalReport.TIME);
|
||||
|
||||
private static final Pattern LAT_LON_PTRN = Pattern
|
||||
.compile(InternalReport.LATLON);
|
||||
|
||||
private static final Pattern STATION_ID_PTRN = Pattern
|
||||
.compile(InternalReport.STATIONID);
|
||||
|
||||
private static final Pattern yearPtrn = Pattern.compile("\\d{4,4}");
|
||||
|
||||
private static final Pattern monthPtrn = Pattern
|
||||
private static final Pattern MONTH_PTRN = Pattern
|
||||
.compile("(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)");
|
||||
|
||||
private static final String[] EMPTY_ARR = new String[0];
|
||||
|
||||
static {
|
||||
MONTH_MAP.put("JAN", 1);
|
||||
MONTH_MAP.put("FEB", 2);
|
||||
|
@ -144,7 +115,7 @@ public class SvrWxParser {
|
|||
|
||||
/**
|
||||
* SvrWx Parser.
|
||||
*
|
||||
*
|
||||
* @param dao
|
||||
* @param pdd
|
||||
* @param name
|
||||
|
@ -157,7 +128,7 @@ public class SvrWxParser {
|
|||
|
||||
/**
|
||||
* Set the message data and decode all message reports.
|
||||
*
|
||||
*
|
||||
* @param message
|
||||
* Raw message data.
|
||||
* @param traceId
|
||||
|
@ -166,23 +137,11 @@ public class SvrWxParser {
|
|||
*/
|
||||
public void setData(byte[] message, String traceId, Headers headers) {
|
||||
currentReport = -1;
|
||||
badLines = new ArrayList<String>();
|
||||
this.traceId = traceId;
|
||||
String fileName = (String) headers.get(WMOHeader.INGEST_FILE_NAME);
|
||||
wmoHeader = new WMOHeader(message, fileName);
|
||||
if (wmoHeader != null) {
|
||||
reports = findReports(message);
|
||||
if (!badLines.isEmpty()) {
|
||||
StringBuilder warnMsg = new StringBuilder("Message ");
|
||||
warnMsg.append(wmoHeader);
|
||||
warnMsg.append(" skipped lines:");
|
||||
for (String s : badLines) {
|
||||
warnMsg.append("\nUnrecognized location: ");
|
||||
warnMsg.append(s);
|
||||
}
|
||||
logger.warn(warnMsg.toString());
|
||||
badLines.clear();
|
||||
}
|
||||
} else {
|
||||
logger.error(traceId + "- Missing or invalid WMOHeader");
|
||||
}
|
||||
|
@ -193,7 +152,7 @@ public class SvrWxParser {
|
|||
|
||||
/**
|
||||
* Does this parser contain any more reports.
|
||||
*
|
||||
*
|
||||
* @return Does this parser contain any more reports.
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
|
@ -211,7 +170,7 @@ public class SvrWxParser {
|
|||
/**
|
||||
* Get the next available report. Returns a null reference if no more
|
||||
* reports are available.
|
||||
*
|
||||
*
|
||||
* @return The next available report.
|
||||
*/
|
||||
public SvrWxRecord next() {
|
||||
|
@ -254,7 +213,7 @@ public class SvrWxParser {
|
|||
|
||||
/**
|
||||
* Gets Container
|
||||
*
|
||||
*
|
||||
* @param obsData
|
||||
* @return
|
||||
*/
|
||||
|
@ -271,7 +230,7 @@ public class SvrWxParser {
|
|||
|
||||
/**
|
||||
* Collect Reports from svrWx Records.
|
||||
*
|
||||
*
|
||||
* @param message
|
||||
* @return reports
|
||||
*/
|
||||
|
@ -281,211 +240,319 @@ public class SvrWxParser {
|
|||
|
||||
List<InternalReport> parts = InternalReport.identifyMessage(message);
|
||||
if (parts != null) {
|
||||
SvrWxRecord svrWxRecord;
|
||||
clearData();
|
||||
EventReport eRpt;
|
||||
String[] missingFields;
|
||||
int month = -1;
|
||||
int year = -1;
|
||||
int reportCount = 0;
|
||||
int invalidCount = 0;
|
||||
boolean allDropped = false;
|
||||
for (InternalReport rpt : parts) {
|
||||
switch (rpt.getLineType()) {
|
||||
case TIME_RANGE:
|
||||
parseTimeRangeLine(rpt.getReportLine());
|
||||
month = parseMonth(rpt.getReportLine());
|
||||
year = parseYear(rpt.getReportLine());
|
||||
break;
|
||||
case EVENT_REPORT:
|
||||
reportCount++;
|
||||
eRpt = (EventReport) rpt;
|
||||
missingFields = getMissingFields(eRpt);
|
||||
if (missingFields.length == 0 && year != -1 && month != -1) {
|
||||
reports.add(buildRecord(eRpt, month, year));
|
||||
} else {
|
||||
if (year == -1 || month == -1) {
|
||||
if (!allDropped) {
|
||||
logger.warn(this.traceId
|
||||
+ " - No time range found. All records"
|
||||
+ " will be discarded.");
|
||||
allDropped = true;
|
||||
}
|
||||
} else {
|
||||
logInvalidReport(eRpt, missingFields);
|
||||
}
|
||||
invalidCount++;
|
||||
}
|
||||
case REPORT_TYPE:
|
||||
if ((reportType != null && eventKey != null)
|
||||
&& isStationOk()) {
|
||||
SurfaceObsLocation location = new SurfaceObsLocation(
|
||||
stationId);
|
||||
location.setLongitude(longitude.doubleValue());
|
||||
location.setLatitude(latitude.doubleValue());
|
||||
svrWxRecord = new SvrWxRecord();
|
||||
svrWxRecord.setReportType(reportType);
|
||||
svrWxRecord.setGreenTime(greenTime);
|
||||
svrWxRecord.setLocation(location);
|
||||
svrWxRecord.setDataTime(refTime);
|
||||
svrWxRecord.setEventKey(eventKey);
|
||||
svrWxRecord.setDetails(getDetails());
|
||||
reports.add(svrWxRecord);
|
||||
}
|
||||
clearData();
|
||||
break;
|
||||
case EVENT_LN:
|
||||
if ((reportType != null && eventKey != null)
|
||||
&& isStationOk()) {
|
||||
SurfaceObsLocation location = new SurfaceObsLocation(
|
||||
stationId);
|
||||
location.setLongitude(longitude.doubleValue());
|
||||
location.setLatitude(latitude.doubleValue());
|
||||
svrWxRecord = new SvrWxRecord();
|
||||
svrWxRecord.setReportType(reportType);
|
||||
svrWxRecord.setGreenTime(greenTime);
|
||||
svrWxRecord.setLocation(location);
|
||||
svrWxRecord.setDataTime(refTime);
|
||||
svrWxRecord.setEventKey(eventKey);
|
||||
svrWxRecord.setDetails(getDetails());
|
||||
reports.add(svrWxRecord);
|
||||
}
|
||||
clearData();
|
||||
parseEventKeyLine(rpt.getReportLine());
|
||||
break;
|
||||
case REMARKS:
|
||||
parseRemarksLine(rpt.getReportLine());
|
||||
break;
|
||||
case EXTRA:
|
||||
String s = rpt.getReportLine().trim();
|
||||
if ((s.length() != 0) && (remarks != null)) {
|
||||
remarks += " " + s;
|
||||
}
|
||||
if (s.length() != 0 && eventKey != null && !isStationOk()) {
|
||||
badLines.add(s);
|
||||
}
|
||||
break;
|
||||
case END:
|
||||
if ((reportType != null && eventKey != null)
|
||||
&& isStationOk()) {
|
||||
SurfaceObsLocation location = new SurfaceObsLocation(
|
||||
stationId);
|
||||
location.setLongitude(longitude.doubleValue());
|
||||
location.setLatitude(latitude.doubleValue());
|
||||
svrWxRecord = new SvrWxRecord();
|
||||
svrWxRecord.setReportType(reportType);
|
||||
svrWxRecord.setGreenTime(greenTime);
|
||||
svrWxRecord.setLocation(location);
|
||||
svrWxRecord.setDataTime(refTime);
|
||||
svrWxRecord.setDetails(getDetails());
|
||||
svrWxRecord.setEventKey(eventKey);
|
||||
reports.add(svrWxRecord);
|
||||
}
|
||||
clearData();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidCount > 0) {
|
||||
logger.warn("Discarded " + invalidCount + "/" + reportCount
|
||||
+ " reports.");
|
||||
}
|
||||
}
|
||||
return reports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Time Range Line.
|
||||
*
|
||||
* @param timeRangeLine
|
||||
* Builds a SvrWxRecord from an EventReport.
|
||||
*
|
||||
* @param eRpt
|
||||
* The EventReport.
|
||||
* @param month
|
||||
* The previously parsed month.
|
||||
* @param year
|
||||
* The previously parsed year.
|
||||
* @return The constructed SvrWxRecord.
|
||||
*/
|
||||
private void parseTimeRangeLine(String timeRangeLine) {
|
||||
Matcher m = monthPtrn.matcher(timeRangeLine);
|
||||
private SvrWxRecord buildRecord(EventReport eRpt, int month, int year) {
|
||||
SurfaceObsLocation location = new SurfaceObsLocation(
|
||||
eRpt.getStationId());
|
||||
location.setLongitude(getLon(eRpt.getLatLon()));
|
||||
location.setLatitude(getLat(eRpt.getLatLon()));
|
||||
|
||||
SvrWxRecord svrWxRecord = new SvrWxRecord();
|
||||
svrWxRecord.setReportType(getReportType(eRpt
|
||||
.getKey()));
|
||||
svrWxRecord.setGreenTime(getGreenTime(eRpt
|
||||
.getTime()));
|
||||
svrWxRecord.setLocation(location);
|
||||
svrWxRecord.setDataTime(getRefTime(eRpt.getTime(),
|
||||
month, year));
|
||||
svrWxRecord
|
||||
.setEventKey(getEventKey(eRpt.getKey()));
|
||||
svrWxRecord.setDetails(getDetails(svrWxRecord, eRpt));
|
||||
return svrWxRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an EventReport that is invalid.
|
||||
*
|
||||
* @param eRpt
|
||||
* The EventReport.
|
||||
* @param missingFields
|
||||
* The missing fields.
|
||||
*/
|
||||
private void logInvalidReport(EventReport eRpt, String[] missingFields) {
|
||||
StringBuilder errorSb = new StringBuilder()
|
||||
.append("The following report is missing the required ")
|
||||
.append(missingFields.length > 1 ? "fields " : "field ")
|
||||
.append(Arrays.toString(missingFields))
|
||||
.append(" and will be skipped.\n").append(eRpt.toString());
|
||||
|
||||
logger.warn(errorSb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the month.
|
||||
*
|
||||
* @param timeRangeLine
|
||||
* The time range line.
|
||||
* @return The month, or 0 if the month cannot be found.
|
||||
*/
|
||||
private int parseMonth(String timeRangeLine) {
|
||||
int month;
|
||||
Matcher m = MONTH_PTRN.matcher(timeRangeLine);
|
||||
if (m.find()) {
|
||||
month = MONTH_MAP.get(m.group());
|
||||
} else {
|
||||
month = 0;
|
||||
}
|
||||
m = yearPtrn.matcher(timeRangeLine);
|
||||
return month;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the year from the time range line.
|
||||
*
|
||||
* @param timeRangeLine
|
||||
* The time range line.
|
||||
* @return The year, or 0 f the year cannot be found.
|
||||
*/
|
||||
private int parseYear(String timeRangeLine) {
|
||||
int year;
|
||||
Matcher m = YEAR_PTRN.matcher(timeRangeLine);
|
||||
if (m.find()) {
|
||||
year = Integer.parseInt(m.group());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Event Key Line.
|
||||
*
|
||||
* @param eventKeyLine
|
||||
*/
|
||||
private void parseEventKeyLine(String eventKeyLine) {
|
||||
Matcher m = EVENT_KEY_PTRN.matcher(eventKeyLine);
|
||||
if (m.find()) {
|
||||
String type = m.group();
|
||||
if (type.equals(TORN)) {
|
||||
eventKey = reportType = "T";
|
||||
} else if (type.equals(WNDG)) {
|
||||
eventKey = reportType = "W";
|
||||
} else if (type.startsWith("G")) {
|
||||
eventKey = type.replace(" ", "");
|
||||
reportType = "W";
|
||||
} else if (type.startsWith("A")) {
|
||||
eventKey = type.replace(" ", "");
|
||||
reportType = "A";
|
||||
}
|
||||
}
|
||||
|
||||
m = DATE_TIME_PTRN.matcher(eventKeyLine);
|
||||
if (m.find()) {
|
||||
String time = m.group();
|
||||
greenTime = time.replace("/", ".");
|
||||
String[] parts = time.split("/");
|
||||
int day = Integer.parseInt(parts[0]);
|
||||
int hour = Integer.parseInt(parts[1].substring(0, 2)) + 6; // CST to
|
||||
// GMT
|
||||
int minute = Integer.parseInt(parts[1].substring(2));
|
||||
|
||||
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
cal.set(Calendar.YEAR, year);
|
||||
cal.set(Calendar.MONTH, month - 1);
|
||||
cal.set(Calendar.DAY_OF_MONTH, day);
|
||||
cal.set(Calendar.HOUR_OF_DAY, hour);
|
||||
cal.set(Calendar.MINUTE, minute);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
refTime = new DataTime(cal);
|
||||
} else {
|
||||
refTime = null;
|
||||
year = 0;
|
||||
}
|
||||
|
||||
return year;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Remarks Line.
|
||||
*
|
||||
* @param remarksLine
|
||||
* Gets the green time from the time field.
|
||||
*
|
||||
* @param time
|
||||
* The time field.
|
||||
* @return The green time, or null if the time is null.
|
||||
*/
|
||||
private void parseRemarksLine(String remarksLine) {
|
||||
Matcher m = LAT_LON_PTRN.matcher(remarksLine);
|
||||
if (m.find()) {
|
||||
String latLon = m.group();
|
||||
String latStr = latLon.substring(0, 4);
|
||||
String lonStr = latLon.substring(4).trim();
|
||||
latitude = new Float(latStr) / 100;
|
||||
longitude = Float.parseFloat(lonStr) / -100;
|
||||
private String getGreenTime(String time) {
|
||||
if (time == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
m = STATION_ID_PTRN.matcher(remarksLine);
|
||||
if (m.find()) {
|
||||
stationId = m.group();
|
||||
}
|
||||
|
||||
if (stationId != null) {
|
||||
remarks = remarksLine.substring(0, remarksLine.indexOf(stationId))
|
||||
.trim();
|
||||
}
|
||||
return time.replace("/", ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear SvrWx Record.
|
||||
* Gets the ref time from the time field, month, and year.
|
||||
*
|
||||
* @param time
|
||||
* The time field.
|
||||
* @param month
|
||||
* The month.
|
||||
* @param year
|
||||
* The year.
|
||||
* @return The ref time, or null if the time field is null.
|
||||
*/
|
||||
private void clearData() {
|
||||
eventKey = null;
|
||||
refTime = null;
|
||||
remarks = null;
|
||||
stationId = null;
|
||||
latitude = null;
|
||||
longitude = null;
|
||||
greenTime = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get details of SvrWx Record.
|
||||
*
|
||||
* @return details
|
||||
*/
|
||||
private String getDetails() {
|
||||
String details = eventKey + " " + greenTime + ":";
|
||||
if (stationId != null) {
|
||||
details += " " + stationId;
|
||||
private DataTime getRefTime(String time, int month, int year) {
|
||||
if (time == null) {
|
||||
return null;
|
||||
}
|
||||
details += " " + remarks;
|
||||
return details;
|
||||
|
||||
String[] parts = time.split("/");
|
||||
int day = Integer.parseInt(parts[0]);
|
||||
int hour = Integer.parseInt(parts[1].substring(0, 2)) + 6; // CST to
|
||||
// GMT
|
||||
int minute = Integer.parseInt(parts[1].substring(2));
|
||||
|
||||
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
cal.set(Calendar.YEAR, year);
|
||||
cal.set(Calendar.MONTH, month - 1);
|
||||
cal.set(Calendar.DAY_OF_MONTH, day);
|
||||
cal.set(Calendar.HOUR_OF_DAY, hour);
|
||||
cal.set(Calendar.MINUTE, minute);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
return new DataTime(cal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if parsed location is valid. If it returns false it indicates the
|
||||
* station or latitude/longitude was not parsed from the product since it
|
||||
* didn't match.
|
||||
*
|
||||
* @return true if location is valid.
|
||||
* Gets the report type.
|
||||
*
|
||||
* @param key
|
||||
* The event key field.
|
||||
* @return The report type, or null if the key field is not expected.
|
||||
*/
|
||||
private boolean isStationOk() {
|
||||
return longitude != null && latitude != null && stationId != null;
|
||||
private String getReportType(String key) {
|
||||
String reportType;
|
||||
|
||||
if (key.equals(TORN)) {
|
||||
reportType = "T";
|
||||
} else if (key.equals(WNDG) || key.startsWith("G")) {
|
||||
reportType = "W";
|
||||
} else if (key.startsWith("A")) {
|
||||
reportType = "A";
|
||||
} else {
|
||||
reportType = null;
|
||||
}
|
||||
|
||||
return reportType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the event key.
|
||||
*
|
||||
* @param key
|
||||
* The event key field.
|
||||
* @return The event key, or null if the event key is not expected.
|
||||
*/
|
||||
private String getEventKey(String key) {
|
||||
String eventKey;
|
||||
|
||||
if (key.equals(TORN)) {
|
||||
// Tornado
|
||||
eventKey = "T";
|
||||
} else if (key.equals(WNDG)) {
|
||||
// Wind damage
|
||||
eventKey = "W";
|
||||
} else if (key.startsWith("G") || key.startsWith("A")) {
|
||||
// A nnn Hailstones and diameter in inches. 475 would be 4.75 inches
|
||||
// G nnn Wind gust and speed in knots
|
||||
eventKey = key.replace(" ", "");
|
||||
} else {
|
||||
eventKey = null;
|
||||
}
|
||||
|
||||
return eventKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latitude as a double. Northern hemisphere is assumed.
|
||||
*
|
||||
* @param latlon
|
||||
* The latitude/longitude String.
|
||||
* @return The latitude as a double.
|
||||
*/
|
||||
private double getLat(String latlon) {
|
||||
return Float.parseFloat(parseLat(latlon)) / 100.0D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the longitude as a double. Western hemisphere is assumed.
|
||||
*
|
||||
* @param latlon
|
||||
* The latitude/longitude String.
|
||||
* @return The longitude as a double.
|
||||
*/
|
||||
private double getLon(String latlon) {
|
||||
return Float.parseFloat(parseLon(latlon)) / -100.0D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the latitude from the combined latitude/longitude field.
|
||||
*
|
||||
* @param latlon
|
||||
* The combined latitude/longitude field.
|
||||
* @return The latitude.
|
||||
*/
|
||||
private String parseLat(String latlon) {
|
||||
return latlon.substring(0, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the longitude from the combined latitude/longitude field.
|
||||
*
|
||||
* @param latlon
|
||||
* The combined latitude/longitude field.
|
||||
* @return The longitude.
|
||||
*/
|
||||
private String parseLon(String latlon) {
|
||||
return latlon.substring(4).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event details.
|
||||
*
|
||||
* @param record
|
||||
* The SvrWxRecord.
|
||||
* @param eRpt
|
||||
* The event report.
|
||||
* @return The event details.
|
||||
*/
|
||||
private String getDetails(SvrWxRecord record, EventReport eRpt) {
|
||||
StringBuilder details = new StringBuilder()
|
||||
.append(record.getEventKey()).append(" ")
|
||||
.append(record.getGreenTime()).append(":")
|
||||
.append(record.getStationId()).append(" ")
|
||||
.append(eRpt.getRemarks());
|
||||
|
||||
return details.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for missing fields that are required.
|
||||
*
|
||||
* @param eRpt
|
||||
* The event report.
|
||||
* @return An array of the missing fields' names, or an empty array if no
|
||||
* fields are missing.
|
||||
*/
|
||||
private String[] getMissingFields(EventReport eRpt) {
|
||||
|
||||
List<String> missing = new ArrayList<String>();
|
||||
if (eRpt.getStationId() == null) {
|
||||
missing.add("StationID");
|
||||
}
|
||||
|
||||
if (eRpt.getLatLon() == null) {
|
||||
missing.add("Latitude/Longitude");
|
||||
}
|
||||
|
||||
return missing.isEmpty() ? EMPTY_ARR : missing.toArray(EMPTY_ARR);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue