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-ManifestVersion: 2
|
||||||
Bundle-Name: Svrwx Plug-in
|
Bundle-Name: Svrwx Plug-in
|
||||||
Bundle-SymbolicName: com.raytheon.uf.edex.plugin.svrwx
|
Bundle-SymbolicName: com.raytheon.uf.edex.plugin.svrwx
|
||||||
Bundle-Version: 1.12.1174.qualifier
|
Bundle-Version: 1.14.0.qualifier
|
||||||
Bundle-Vendor: RAYTHEON
|
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",
|
Require-Bundle: com.raytheon.uf.common.dataplugin.svrwx;bundle-version="1.0.0",
|
||||||
com.raytheon.uf.common.pointdata,
|
com.raytheon.uf.common.pointdata,
|
||||||
com.raytheon.uf.edex.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,6 +42,7 @@ import com.raytheon.uf.common.status.UFStatus;
|
||||||
* ------------ ---------- ----------- --------------------------
|
* ------------ ---------- ----------- --------------------------
|
||||||
* Jan 10, 2010 jsanchez Initial creation
|
* Jan 10, 2010 jsanchez Initial creation
|
||||||
* Apr 10, 2014 2971 skorolev Cleaned code.
|
* Apr 10, 2014 2971 skorolev Cleaned code.
|
||||||
|
* Jun 25, 2014 3008 nabowle Refactor for EventReport.
|
||||||
*
|
*
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
@ -74,8 +75,6 @@ public class InternalReport {
|
||||||
public static final String RMK_LN = "^((.*)" + STATIONID + "(.*)" + LATLON
|
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 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
|
public static final String TIME_RANGE_LN = "^((.*)FOR\\s" + REFTIME
|
||||||
|
@ -173,20 +172,25 @@ public class InternalReport {
|
||||||
List<InternalReport> reports = new ArrayList<InternalReport>();
|
List<InternalReport> reports = new ArrayList<InternalReport>();
|
||||||
List<String> lines = separateLines(message);
|
List<String> lines = separateLines(message);
|
||||||
if (lines != null) {
|
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,
|
Pattern patterns[] = { REPORT_TYPE_LN_PTRN, TIME_RANGE_LN_PTRN };
|
||||||
RMK_LN_PTRN, TIME_RANGE_LN_PTRN };
|
InternalType types[] = { InternalType.REPORT_TYPE,
|
||||||
InternalType types[] = { t1, t2, t3, t4 };
|
InternalType.TIME_RANGE };
|
||||||
|
|
||||||
boolean found;
|
boolean found;
|
||||||
|
EventReport.Builder builder = null;
|
||||||
|
Matcher m;
|
||||||
for (String s : lines) {
|
for (String s : lines) {
|
||||||
found = false;
|
found = false;
|
||||||
for (int i = 0; i < patterns.length; i++) {
|
for (int i = 0; i < patterns.length; i++) {
|
||||||
Matcher m = patterns[i].matcher(s);
|
m = patterns[i].matcher(s);
|
||||||
if (m.matches()) {
|
if (m.matches()) {
|
||||||
|
|
||||||
|
if (builder != null) {
|
||||||
|
reports.add(builder.build());
|
||||||
|
builder = null;
|
||||||
|
}
|
||||||
|
|
||||||
InternalReport rptLine = new InternalReport(types[i], s);
|
InternalReport rptLine = new InternalReport(types[i], s);
|
||||||
reports.add(rptLine);
|
reports.add(rptLine);
|
||||||
found = true;
|
found = true;
|
||||||
|
@ -194,13 +198,35 @@ public class InternalReport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
|
// 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(
|
InternalReport rptLine = new InternalReport(
|
||||||
InternalType.EXTRA, s);
|
InternalType.EXTRA, s);
|
||||||
reports.add(rptLine);
|
reports.add(rptLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InternalReport rptLine = new InternalReport(InternalType.END, "");
|
}
|
||||||
reports.add(rptLine);
|
|
||||||
|
if (builder != null) {
|
||||||
|
reports.add(builder.build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return reports;
|
return reports;
|
||||||
}
|
}
|
||||||
|
@ -222,7 +248,7 @@ public class InternalReport {
|
||||||
String s;
|
String s;
|
||||||
reportLines = new ArrayList<String>();
|
reportLines = new ArrayList<String>();
|
||||||
while ((s = reader.readLine()) != null) {
|
while ((s = reader.readLine()) != null) {
|
||||||
if (s.length() > 0) {
|
if (s.trim().length() > 0) {
|
||||||
reportLines.add(s);
|
reportLines.add(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ package com.raytheon.uf.edex.plugin.svrwx.decoder;
|
||||||
* ------------ ---------- ----------- --------------------------
|
* ------------ ---------- ----------- --------------------------
|
||||||
* Jan 05, 2010 jsanchez Initial creation
|
* Jan 05, 2010 jsanchez Initial creation
|
||||||
* Apr 10, 2014 skorolev Cleaned code.
|
* Apr 10, 2014 skorolev Cleaned code.
|
||||||
|
* Jun 25, 2014 3008 nabowle Removed unused values.
|
||||||
*
|
*
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
@ -37,5 +38,5 @@ package com.raytheon.uf.edex.plugin.svrwx.decoder;
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
*/
|
*/
|
||||||
public enum InternalType {
|
public enum InternalType {
|
||||||
TIME_RANGE, REPORT_TYPE, EVENT_LN, REMARKS, EXTRA, END, WRONG_LN;
|
TIME_RANGE, REPORT_TYPE, EXTRA, EVENT_REPORT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package com.raytheon.uf.edex.plugin.svrwx.decoder;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -44,8 +45,10 @@ import com.raytheon.uf.edex.plugin.svrwx.SvrWxRecordDao;
|
||||||
/**
|
/**
|
||||||
* SvrWx Parser
|
* SvrWx Parser
|
||||||
*
|
*
|
||||||
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
*
|
*
|
||||||
|
*
|
||||||
* SOFTWARE HISTORY
|
* SOFTWARE HISTORY
|
||||||
* Date Ticket# Engineer Description
|
* Date Ticket# Engineer Description
|
||||||
* ------------ ---------- ----------- --------------------------
|
* ------------ ---------- ----------- --------------------------
|
||||||
|
@ -53,6 +56,7 @@ import com.raytheon.uf.edex.plugin.svrwx.SvrWxRecordDao;
|
||||||
* Aug 30, 2013 2298 rjpeter Make getPluginName abstract
|
* Aug 30, 2013 2298 rjpeter Make getPluginName abstract
|
||||||
* Apr 07, 2014 2971 skorolev Add condition to avoid malformed parts in the message.
|
* 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
|
* May 14, 2014 2536 bclement moved WMO Header to common, removed pluginName
|
||||||
|
* Jun 25, 2014 3008 nabowle Refactor for EventReport type
|
||||||
*
|
*
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
@ -81,52 +85,19 @@ public class SvrWxParser {
|
||||||
|
|
||||||
private List<SvrWxRecord> reports;
|
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 TORN = "*TORN";
|
||||||
|
|
||||||
public static final String WNDG = "WNDG";
|
public static final String WNDG = "WNDG";
|
||||||
|
|
||||||
private static final HashMap<String, Integer> MONTH_MAP = new HashMap<String, Integer>();
|
private static final HashMap<String, Integer> MONTH_MAP = new HashMap<String, Integer>();
|
||||||
|
|
||||||
/** List of lines with non-parsed location. */
|
private static final Pattern YEAR_PTRN = Pattern.compile("\\d{4,4}");
|
||||||
private List<String> badLines;
|
|
||||||
|
|
||||||
private static final Pattern EVENT_KEY_PTRN = Pattern
|
private static final Pattern MONTH_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
|
|
||||||
.compile("(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)");
|
.compile("(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)");
|
||||||
|
|
||||||
|
private static final String[] EMPTY_ARR = new String[0];
|
||||||
|
|
||||||
static {
|
static {
|
||||||
MONTH_MAP.put("JAN", 1);
|
MONTH_MAP.put("JAN", 1);
|
||||||
MONTH_MAP.put("FEB", 2);
|
MONTH_MAP.put("FEB", 2);
|
||||||
|
@ -166,23 +137,11 @@ public class SvrWxParser {
|
||||||
*/
|
*/
|
||||||
public void setData(byte[] message, String traceId, Headers headers) {
|
public void setData(byte[] message, String traceId, Headers headers) {
|
||||||
currentReport = -1;
|
currentReport = -1;
|
||||||
badLines = new ArrayList<String>();
|
|
||||||
this.traceId = traceId;
|
this.traceId = traceId;
|
||||||
String fileName = (String) headers.get(WMOHeader.INGEST_FILE_NAME);
|
String fileName = (String) headers.get(WMOHeader.INGEST_FILE_NAME);
|
||||||
wmoHeader = new WMOHeader(message, fileName);
|
wmoHeader = new WMOHeader(message, fileName);
|
||||||
if (wmoHeader != null) {
|
if (wmoHeader != null) {
|
||||||
reports = findReports(message);
|
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 {
|
} else {
|
||||||
logger.error(traceId + "- Missing or invalid WMOHeader");
|
logger.error(traceId + "- Missing or invalid WMOHeader");
|
||||||
}
|
}
|
||||||
|
@ -281,128 +240,169 @@ public class SvrWxParser {
|
||||||
|
|
||||||
List<InternalReport> parts = InternalReport.identifyMessage(message);
|
List<InternalReport> parts = InternalReport.identifyMessage(message);
|
||||||
if (parts != null) {
|
if (parts != null) {
|
||||||
SvrWxRecord svrWxRecord;
|
EventReport eRpt;
|
||||||
clearData();
|
String[] missingFields;
|
||||||
|
int month = -1;
|
||||||
|
int year = -1;
|
||||||
|
int reportCount = 0;
|
||||||
|
int invalidCount = 0;
|
||||||
|
boolean allDropped = false;
|
||||||
for (InternalReport rpt : parts) {
|
for (InternalReport rpt : parts) {
|
||||||
switch (rpt.getLineType()) {
|
switch (rpt.getLineType()) {
|
||||||
case TIME_RANGE:
|
case TIME_RANGE:
|
||||||
parseTimeRangeLine(rpt.getReportLine());
|
month = parseMonth(rpt.getReportLine());
|
||||||
|
year = parseYear(rpt.getReportLine());
|
||||||
break;
|
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:
|
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:
|
case EXTRA:
|
||||||
String s = rpt.getReportLine().trim();
|
default:
|
||||||
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();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (invalidCount > 0) {
|
||||||
|
logger.warn("Discarded " + invalidCount + "/" + reportCount
|
||||||
|
+ " reports.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return reports;
|
return reports;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse Time Range Line.
|
* Builds a SvrWxRecord from an EventReport.
|
||||||
*
|
*
|
||||||
* @param timeRangeLine
|
* @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) {
|
private SvrWxRecord buildRecord(EventReport eRpt, int month, int year) {
|
||||||
Matcher m = monthPtrn.matcher(timeRangeLine);
|
SurfaceObsLocation location = new SurfaceObsLocation(
|
||||||
if (m.find()) {
|
eRpt.getStationId());
|
||||||
month = MONTH_MAP.get(m.group());
|
location.setLongitude(getLon(eRpt.getLatLon()));
|
||||||
}
|
location.setLatitude(getLat(eRpt.getLatLon()));
|
||||||
m = yearPtrn.matcher(timeRangeLine);
|
|
||||||
if (m.find()) {
|
SvrWxRecord svrWxRecord = new SvrWxRecord();
|
||||||
year = Integer.parseInt(m.group());
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse Event Key Line.
|
* Logs an EventReport that is invalid.
|
||||||
*
|
*
|
||||||
* @param eventKeyLine
|
* @param eRpt
|
||||||
|
* The EventReport.
|
||||||
|
* @param missingFields
|
||||||
|
* The missing fields.
|
||||||
*/
|
*/
|
||||||
private void parseEventKeyLine(String eventKeyLine) {
|
private void logInvalidReport(EventReport eRpt, String[] missingFields) {
|
||||||
Matcher m = EVENT_KEY_PTRN.matcher(eventKeyLine);
|
StringBuilder errorSb = new StringBuilder()
|
||||||
if (m.find()) {
|
.append("The following report is missing the required ")
|
||||||
String type = m.group();
|
.append(missingFields.length > 1 ? "fields " : "field ")
|
||||||
if (type.equals(TORN)) {
|
.append(Arrays.toString(missingFields))
|
||||||
eventKey = reportType = "T";
|
.append(" and will be skipped.\n").append(eRpt.toString());
|
||||||
} else if (type.equals(WNDG)) {
|
|
||||||
eventKey = reportType = "W";
|
logger.warn(errorSb.toString());
|
||||||
} 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);
|
/**
|
||||||
|
* 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()) {
|
if (m.find()) {
|
||||||
String time = m.group();
|
month = MONTH_MAP.get(m.group());
|
||||||
greenTime = time.replace("/", ".");
|
} else {
|
||||||
|
month = 0;
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
} else {
|
||||||
|
year = 0;
|
||||||
|
}
|
||||||
|
return year;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 String getGreenTime(String time) {
|
||||||
|
if (time == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.replace("/", ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 DataTime getRefTime(String time, int month, int year) {
|
||||||
|
if (time == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
String[] parts = time.split("/");
|
String[] parts = time.split("/");
|
||||||
int day = Integer.parseInt(parts[0]);
|
int day = Integer.parseInt(parts[0]);
|
||||||
int hour = Integer.parseInt(parts[1].substring(0, 2)) + 6; // CST to
|
int hour = Integer.parseInt(parts[1].substring(0, 2)) + 6; // CST to
|
||||||
|
@ -418,74 +418,141 @@ public class SvrWxParser {
|
||||||
cal.set(Calendar.SECOND, 0);
|
cal.set(Calendar.SECOND, 0);
|
||||||
cal.set(Calendar.MILLISECOND, 0);
|
cal.set(Calendar.MILLISECOND, 0);
|
||||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||||
refTime = new DataTime(cal);
|
return new DataTime(cal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the report type.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* The event key field.
|
||||||
|
* @return The report type, or null if the key field is not expected.
|
||||||
|
*/
|
||||||
|
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 {
|
} else {
|
||||||
refTime = null;
|
reportType = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return reportType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse Remarks Line.
|
* Gets the event key.
|
||||||
*
|
*
|
||||||
* @param remarksLine
|
* @param key
|
||||||
|
* The event key field.
|
||||||
|
* @return The event key, or null if the event key is not expected.
|
||||||
*/
|
*/
|
||||||
private void parseRemarksLine(String remarksLine) {
|
private String getEventKey(String key) {
|
||||||
Matcher m = LAT_LON_PTRN.matcher(remarksLine);
|
String eventKey;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
m = STATION_ID_PTRN.matcher(remarksLine);
|
if (key.equals(TORN)) {
|
||||||
if (m.find()) {
|
// Tornado
|
||||||
stationId = m.group();
|
eventKey = "T";
|
||||||
}
|
} else if (key.equals(WNDG)) {
|
||||||
|
// Wind damage
|
||||||
if (stationId != null) {
|
eventKey = "W";
|
||||||
remarks = remarksLine.substring(0, remarksLine.indexOf(stationId))
|
} else if (key.startsWith("G") || key.startsWith("A")) {
|
||||||
.trim();
|
// 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 {
|
||||||
/**
|
|
||||||
* Clear SvrWx Record.
|
|
||||||
*/
|
|
||||||
private void clearData() {
|
|
||||||
eventKey = null;
|
eventKey = null;
|
||||||
refTime = null;
|
}
|
||||||
remarks = null;
|
|
||||||
stationId = null;
|
return eventKey;
|
||||||
latitude = null;
|
|
||||||
longitude = null;
|
|
||||||
greenTime = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get details of SvrWx Record.
|
* Get the latitude as a double. Northern hemisphere is assumed.
|
||||||
*
|
*
|
||||||
* @return details
|
* @param latlon
|
||||||
|
* The latitude/longitude String.
|
||||||
|
* @return The latitude as a double.
|
||||||
*/
|
*/
|
||||||
private String getDetails() {
|
private double getLat(String latlon) {
|
||||||
String details = eventKey + " " + greenTime + ":";
|
return Float.parseFloat(parseLat(latlon)) / 100.0D;
|
||||||
if (stationId != null) {
|
|
||||||
details += " " + stationId;
|
|
||||||
}
|
|
||||||
details += " " + remarks;
|
|
||||||
return details;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if parsed location is valid. If it returns false it indicates the
|
* Get the longitude as a double. Western hemisphere is assumed.
|
||||||
* station or latitude/longitude was not parsed from the product since it
|
|
||||||
* didn't match.
|
|
||||||
*
|
*
|
||||||
* @return true if location is valid.
|
* @param latlon
|
||||||
|
* The latitude/longitude String.
|
||||||
|
* @return The longitude as a double.
|
||||||
*/
|
*/
|
||||||
private boolean isStationOk() {
|
private double getLon(String latlon) {
|
||||||
return longitude != null && latitude != null && stationId != null;
|
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