Issue #3488 added bin range check to bin lightning decoder

Change-Id: Ib445da1a33c84283bc8dd46521b6f7c3381a51f9

Former-commit-id: 432f43b567 [formerly 53b274aab9952d372221c686d886ac12e8eb2e6a]
Former-commit-id: 8d928ab3a2
This commit is contained in:
Brian Clements 2014-08-04 14:53:42 -05:00
parent dfc3545bf5
commit ba934c5411

View file

@ -22,8 +22,14 @@ package com.raytheon.edex.plugin.binlightning;
import gov.noaa.nws.ost.edex.plugin.binlightning.BinLigntningDecoderUtil;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
import org.apache.commons.logging.Log;
@ -33,12 +39,12 @@ import com.raytheon.edex.esb.Headers;
import com.raytheon.edex.exception.DecoderException;
import com.raytheon.edex.plugin.AbstractDecoder;
import com.raytheon.uf.common.dataplugin.PluginDataObject;
import com.raytheon.uf.common.dataplugin.PluginException;
import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint;
import com.raytheon.uf.common.dataplugin.binlightning.impl.LtgStrikeType;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.TimeRange;
import com.raytheon.uf.common.time.util.TimeUtil;
import com.raytheon.uf.edex.decodertools.core.DecoderTools;
import com.raytheon.uf.edex.decodertools.time.TimeTools;
import com.raytheon.uf.edex.wmo.message.WMOHeader;
@ -82,6 +88,7 @@ import com.raytheon.uf.edex.wmo.message.WMOHeader;
* Jan 24, 2014 DR 16774 Wufeng Zhou Modified for updated Bin-lightning data spec,
* and to used WMO header to distinguish bit-shifted
* GLD360 and NLDN data.
* Aug 04, 2014 3488 bclement added checkBinRange(), rebin() and finalizeRecords()
*
* </pre>
*
@ -100,6 +107,9 @@ public class BinLightningDecoder extends AbstractDecoder {
private final Log logger = LogFactory.getLog(getClass());
private static final boolean REBIN_INVALID_DATA = Boolean
.getBoolean("rebin.invalid.binlightning");
/**
* Default lightning strike type for FLASH messages. RT_FLASH documents
* indicate no default, but D2D code defaults to STRIKE_CG also.
@ -127,7 +137,7 @@ public class BinLightningDecoder extends AbstractDecoder {
public PluginDataObject[] decode(byte[] data, Headers headers) throws DecoderException {
//String traceId = null;
PluginDataObject[] reports = new PluginDataObject[0];
PluginDataObject[] rval = new PluginDataObject[0];
if (data != null) {
traceId = (String) headers.get(DecoderTools.INGEST_FILE_NAME);
@ -163,11 +173,13 @@ public class BinLightningDecoder extends AbstractDecoder {
// both encrypted data and legacy data
//
List<LightningStrikePoint> strikes = BinLigntningDecoderUtil.decodeBinLightningData(data, pdata, traceId, wmoHdr, baseTime.getTime());
Collection<LightningStrikePoint> strikes = BinLigntningDecoderUtil
.decodeBinLightningData(data, pdata, traceId, wmoHdr,
baseTime.getTime());
if (strikes == null) { // keep-alive record, log and return
logger.info(traceId + " - found keep-alive record. ignore for now.");
return reports;
return rval;
}
//
@ -186,44 +198,133 @@ public class BinLightningDecoder extends AbstractDecoder {
return new PluginDataObject[0];
}
Calendar c = TimeTools.copy(baseTime);
if (c == null) {
throw new DecoderException(traceId + " - Error decoding times");
}
//report.setInsertTime(c); // OB13.4 source code does not have this line anymore, WZ 05/03/2013
Calendar cStart = report.getStartTime();
if (cStart.getTimeInMillis() > (c.getTimeInMillis() + TEN_MINUTES)) {
synchronized (SDF) {
logger.info("Discarding future data for " + traceId
+ " at " + SDF.format(cStart.getTime()));
}
} else {
Calendar cStop = report.getStopTime();
TimeRange range = new TimeRange(cStart.getTimeInMillis(),
cStop.getTimeInMillis());
DataTime dataTime = new DataTime(cStart, range);
report.setDataTime(dataTime);
if (report != null) {
report.setTraceId(traceId);
//report.setPluginName("binlightning"); // line disappear in OB15.5.3
try {
report.constructDataURI();
reports = new PluginDataObject[] { report };
} catch (PluginException e) {
logger.error("Error constructing datauri", e);
throw new DecoderException("Error constructing datauri", e);
}
}
}
Collection<BinLightningRecord> records = checkBinRange(report,
strikes);
rval = finalizeRecords(records, baseTime);
}
} else {
logger.error("No WMOHeader found in data");
}
return reports;
return rval;
}
/**
* Perform final actions on each record and populate a PDO array with them.
* Any invalid records will be omitted from the return array.
*
* @param records
* @param baseTime
* @return
* @throws DecoderException
*/
private PluginDataObject[] finalizeRecords(
Collection<BinLightningRecord> records, Calendar baseTime)
throws DecoderException {
Calendar c = TimeTools.copy(baseTime);
if (c == null) {
throw new DecoderException(traceId + " - Error decoding times");
}
ArrayList<BinLightningRecord> rval = new ArrayList<BinLightningRecord>(
records.size());
for (BinLightningRecord record : records) {
Calendar cStart = record.getStartTime();
if (cStart.getTimeInMillis() > (c.getTimeInMillis() + TEN_MINUTES)) {
synchronized (SDF) {
logger.info("Discarding future data for " + traceId
+ " at " + SDF.format(cStart.getTime()));
}
} else {
Calendar cStop = record.getStopTime();
TimeRange range = new TimeRange(cStart.getTimeInMillis(),
cStop.getTimeInMillis());
DataTime dataTime = new DataTime(cStart, range);
record.setDataTime(dataTime);
if (record != null) {
record.setTraceId(traceId);
rval.add(record);
}
}
}
return rval.toArray(new PluginDataObject[rval.size()]);
}
/**
* Ensure that the record has a valid bin range. If it does, it will be the
* only record in the return value. Otherwise, {@link #REBIN_INVALID_DATA}
* is used to determine if no records should be returned or the strikes
* should be split into valid bin ranges uses {@link #rebin(Collection)}
*
* @param record
* @param strikes
* @return
*/
private Collection<BinLightningRecord> checkBinRange(
BinLightningRecord record, Collection<LightningStrikePoint> strikes) {
Collection<BinLightningRecord> rval = Collections.emptyList();
Calendar cStart = record.getStartTime();
Calendar cStop = record.getStopTime();
long binRange = cStop.getTimeInMillis() - cStart.getTimeInMillis();
if (binRange > TimeUtil.MILLIS_PER_DAY) {
if (REBIN_INVALID_DATA) {
rval = rebin(strikes);
} else {
String rangeStart;
String rangeEnd;
synchronized (SDF) {
rangeStart = SDF.format(cStart.getTime());
rangeEnd = SDF.format(cStop.getTime());
}
logger.error("Discarding data with invalid bin range of "
+ rangeStart + " to " + rangeEnd);
}
} else {
rval = Arrays.asList(record);
}
return rval;
}
/**
* Split the strikes into 1 day bins and create a new record for each bin
*
* @param strikes
* @return
*/
private Collection<BinLightningRecord> rebin(
Collection<LightningStrikePoint> strikes) {
Map<Long, Collection<LightningStrikePoint>> binMap = new HashMap<Long, Collection<LightningStrikePoint>>(
1);
for (LightningStrikePoint strike : strikes) {
Calendar c = TimeTools.getBaseCalendar(strike.getYear(),
strike.getMonth(), strike.getDay());
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
long key = c.getTimeInMillis();
Collection<LightningStrikePoint> bin = binMap.get(key);
if (bin == null) {
bin = new ArrayList<LightningStrikePoint>(strikes.size());
binMap.put(key, bin);
}
bin.add(strike);
}
Collection<BinLightningRecord> rval = new ArrayList<BinLightningRecord>(
binMap.size());
for (Entry<Long, Collection<LightningStrikePoint>> e : binMap
.entrySet()) {
Collection<LightningStrikePoint> bin = e.getValue();
BinLightningRecord record = new BinLightningRecord(bin.size());
for (LightningStrikePoint strike : bin) {
record.addStrike(strike);
}
rval.add(record);
}
return rval;
}
/**