VLab Issue #18075 - DR 18336 fix. All keep-alives were being treated as NLDN. Get source from WMO header instead; fixes #18075

Change-Id: I53a392780148de85de9c25fff05c313737137b57

Former-commit-id: 3cf025add9b0c2636a442784bb977601ed7b434e
This commit is contained in:
Andrew Moore 2016-04-22 16:03:57 +00:00
parent c4e85ee05d
commit de50a8ac01
3 changed files with 98 additions and 41 deletions

View file

@ -101,6 +101,7 @@ import com.raytheon.uf.edex.decodertools.core.DecoderTools;
* Aug 04, 2014 3488 bclement added checkBinRange(), rebin() and finalizeRecords() * Aug 04, 2014 3488 bclement added checkBinRange(), rebin() and finalizeRecords()
* Mar 08, 2016 18336 amoore Keep-alive messages should update the legend. * Mar 08, 2016 18336 amoore Keep-alive messages should update the legend.
* Apr 07, 2016 DR18763 mgamazaychikov Switched to using LightningWMOHeader. * Apr 07, 2016 DR18763 mgamazaychikov Switched to using LightningWMOHeader.
* May 02, 2016 18336 amoore BinLightningRecord constructor takes source.
* *
* </pre> * </pre>
* *
@ -205,7 +206,6 @@ public class BinLightningDecoder {
* and added logic to process both encrypted data and legacy * and added logic to process both encrypted data and legacy
* data * data
*/ */
Collection<LightningStrikePoint> strikes = decodeBinLightningData( Collection<LightningStrikePoint> strikes = decodeBinLightningData(
data, pdata, traceId, wmoHdr, baseTime.getTime()); data, pdata, traceId, wmoHdr, baseTime.getTime());
@ -213,17 +213,23 @@ public class BinLightningDecoder {
* Done MOD by Wufeng Zhou * Done MOD by Wufeng Zhou
*/ */
// post processing data - if not keep-alive record // post processing data
BinLightningRecord report = null; BinLightningRecord report = null;
if (strikes.size() > 0) { if (strikes.size() > 0) {
// not a keep-alive
report = LightningGeoFilter.createFilteredRecord(strikes); report = LightningGeoFilter.createFilteredRecord(strikes);
} else { } else {
// keep-alive, get the source from WMO header
String source = getSourceFromHeader(wmoHdr);
synchronized (SDF) { synchronized (SDF) {
logger.info(traceId logger.info(traceId
+ ": found keep-alive record of base time [" + ": found keep-alive record of base time ["
+ SDF.format(baseTime.getTime()) + "]"); + SDF.format(baseTime.getTime())
+ "] and source [" + source + "]");
} }
report = new BinLightningRecord(baseTime);
report = new BinLightningRecord(baseTime, source);
} }
Collection<BinLightningRecord> records = checkBinRange(report, Collection<BinLightningRecord> records = checkBinRange(report,
@ -395,8 +401,8 @@ public class BinLightningDecoder {
* LightningStrikePoint * LightningStrikePoint
*/ */
public static List<LightningStrikePoint> decodeBinLightningData( public static List<LightningStrikePoint> decodeBinLightningData(
byte[] data, byte[] pdata, String traceId, LightningWMOHeader wmoHdr, byte[] data, byte[] pdata, String traceId,
Date dataDate) { LightningWMOHeader wmoHdr, Date dataDate) {
if (pdata == null) { // if data without header not passed, we'll strip if (pdata == null) { // if data without header not passed, we'll strip
// the WMO header here // the WMO header here
LightningWMOHeader header = new LightningWMOHeader(data); LightningWMOHeader header = new LightningWMOHeader(data);
@ -481,8 +487,8 @@ public class BinLightningDecoder {
return new ArrayList<>(0); return new ArrayList<>(0);
} }
/* /*
* not keep-alive record, then check data validity and decode * not necessarily keep-alive record, then check data validity
* into an ArrayList<LightningStrikePoint> of strikes * and decode into an ArrayList<LightningStrikePoint> of strikes
*/ */
if (BinLightningDecoderUtil if (BinLightningDecoderUtil
.isLightningDataRecords(decryptedData)) { .isLightningDataRecords(decryptedData)) {
@ -563,24 +569,9 @@ public class BinLightningDecoder {
switch (decoder.getError()) { switch (decoder.getError()) {
case IBinLightningDecoder.NO_ERROR: { case IBinLightningDecoder.NO_ERROR: {
for (LightningStrikePoint strike : decoder) { for (LightningStrikePoint strike : decoder) {
/* String source = getSourceFromHeader(wmoHdr);
* use WMO Header to distinguish NLDN or GLD360 data because
* no bit-shifted data spec available for GLD360. strike.setLightSource(source);
* 12/24/2013, WZ The WMO header start string is defined in
* BinLightningAESKey.properties file (normally, GLD360 data
* will have WMO header starts with SFPA41, or SFPA99 for
* test data.)
*/
String gld360WMOHeaderString = BinLightningAESKey
.getProps().getProperty(
"binlightning.gld360WMOHeaderStartString",
"");
if (gld360WMOHeaderString.trim().equals("") == false
&& wmoHdr.getWmoHeader().startsWith(
gld360WMOHeaderString)) {
// GLD360 data based on the setup
strike.setLightSource("GLD");
}
strikes.add(strike); strikes.add(strike);
} }
break; break;
@ -593,6 +584,45 @@ public class BinLightningDecoder {
return strikes; return strikes;
} }
/**
* Get the data source from the given WMO header.
*
* @param wmoHdr
* the header.
* @return the data source according to text from the header.
*/
private static String getSourceFromHeader(LightningWMOHeader wmoHdr) {
/*
* use WMO Header to distinguish NLDN or GLD360 data because no
* bit-shifted data spec available for GLD360. 12/24/2013, WZ The WMO
* header start string is defined in BinLightningAESKey.properties file
* (normally, GLD360 data will have WMO header starts with SFPA41, or
* SFPA99 for test data.)
*/
/*
* continually get the properties locally instead of as a constant
* because the properties file could be changed and reloaded by the
* user.
*/
String gld360WMOHeaderString = BinLightningAESKey.getProps()
.getProperty("binlightning.gld360WMOHeaderStartString", "");
String source = null;
if (gld360WMOHeaderString.trim().equals("") == false
&& wmoHdr.getWmoHeader().startsWith(gld360WMOHeaderString)) {
// GLD360 data based on the setup
source = "GLD";
}
/*
* default source is NLDN, which is the only other option at this time
* for this decoder (5/2/2016, DR 18336). For future sources, add here
* and modify BinLightningAESKey.properties.
*/
return BinLightningRecord.validateSource(source);
}
/** /**
* Set a trace identifier for the source data. * Set a trace identifier for the source data.
* *

View file

@ -62,6 +62,7 @@ import com.raytheon.uf.common.wmo.WMOTimeParser;
* Jun 19, 2014 3226 bclement added validator callback * Jun 19, 2014 3226 bclement added validator callback
* Jul 07, 2015 4581 skorolev Corrected decodeStrikes to avoid BufferUnderflowException. * Jul 07, 2015 4581 skorolev Corrected decodeStrikes to avoid BufferUnderflowException.
* Apr 07, 2016 DR18763 mgamazaychikov Switched to using LightningWMOHeader. * Apr 07, 2016 DR18763 mgamazaychikov Switched to using LightningWMOHeader.
* May 02, 2016 18336 amoore Keep-alive messages should update the legend.
* *
* </pre> * </pre>
* *
@ -205,13 +206,17 @@ public class TotalLightningDecoder {
pdata = decrypt(wmoHdr, fileName, pdata); pdata = decrypt(wmoHdr, fileName, pdata);
} }
List<LightningStrikePoint> strikes = decodeStrikes(fileName, pdata); List<LightningStrikePoint> strikes = decodeStrikes(fileName, pdata);
BinLightningRecord record;
if (!strikes.isEmpty()) { if (!strikes.isEmpty()) {
BinLightningRecord record = LightningGeoFilter record = LightningGeoFilter.createFilteredRecord(strikes);
.createFilteredRecord(strikes);
return new PluginDataObject[] { record };
} else { } else {
return new PluginDataObject[0]; // keep-alive record
Calendar baseTime = WMOTimeParser.findDataTime(wmoHdr.getYYGGgg(),
fileName);
record = new BinLightningRecord(baseTime, DATA_SOURCE);
} }
return new PluginDataObject[] { record };
} }
/** /**
@ -221,8 +226,8 @@ public class TotalLightningDecoder {
* @return * @return
* @throws DecoderException * @throws DecoderException
*/ */
private byte[] decrypt(LightningWMOHeader wmoHdr, String fileName, byte[] pdata) private byte[] decrypt(LightningWMOHeader wmoHdr, String fileName,
throws DecoderException { byte[] pdata) throws DecoderException {
Calendar baseTime = WMOTimeParser.findDataTime(wmoHdr.getYYGGgg(), Calendar baseTime = WMOTimeParser.findDataTime(wmoHdr.getYYGGgg(),
fileName); fileName);
pdata = EncryptedBinLightningCipher.prepDataForDecryption(pdata, pdata = EncryptedBinLightningCipher.prepDataForDecryption(pdata,
@ -236,11 +241,11 @@ public class TotalLightningDecoder {
} }
/** /**
* Extract strike data from raw binary * Extract strike data from raw binary, ignoring keep-alive strikes.
* *
* @param fileName * @param fileName
* @param pdata * @param pdata
* @return * @return list of lightning strike points, ignoring keep-alive strikes.
* @throws DecoderException * @throws DecoderException
*/ */
private List<LightningStrikePoint> decodeStrikes(String fileName, private List<LightningStrikePoint> decodeStrikes(String fileName,

View file

@ -94,6 +94,7 @@ import com.raytheon.uf.common.time.util.TimeUtil;
* Jan 22, 2014 3949 nabowle refactor out default and unknown source constants. * Jan 22, 2014 3949 nabowle refactor out default and unknown source constants.
* Jul 23, 2015 2360 rferrel Add name to unique constraint. * Jul 23, 2015 2360 rferrel Add name to unique constraint.
* Mar 08, 2016 18336 amoore Keep-alive messages should update the legend. * Mar 08, 2016 18336 amoore Keep-alive messages should update the legend.
* May 02, 2016 18336 amoore BinLightningRecord constructor takes source.
* *
* </pre> * </pre>
* *
@ -172,9 +173,11 @@ public class BinLightningRecord extends PersistablePluginDataObject implements
* *
* @param dateTime * @param dateTime
* WMO header base date. * WMO header base date.
* @param vendorSource
* source of the record.
*/ */
public BinLightningRecord(final Calendar dateTime) { public BinLightningRecord(final Calendar dateTime, String vendorSource) {
source = LightningConstants.DEFAULT_SOURCE; source = vendorSource;
// only data shall be datetime for keep-alive // only data shall be datetime for keep-alive
strikeDataArrays.put(LightningConstants.TIME_DATASET, strikeDataArrays.put(LightningConstants.TIME_DATASET,
@ -321,18 +324,37 @@ public class BinLightningRecord extends PersistablePluginDataObject implements
} }
/** /**
* Extract data source from strike * Extract data source from strike.
* *
* @param strike * @param strike
* @return * the strike from which to get data source.
* @return the strike's data source. If null, return
* {@link LightningConstants#DEFAULT_SOURCE}. If empty, return
* {@link LightningConstants#UNKNOWN_SOURCE}.
*/ */
public static String getDataSource(LightningStrikePoint strike) { public static String getDataSource(LightningStrikePoint strike) {
if (strike.getLightSource() == null) { return validateSource(strike.getLightSource());
}
/**
* Examine the lightning source given, and return a pre-defined constant if
* it is empty or null. Return lightning source as-is if non-null and
* non-empty.
*
* @param iSource
* the lightning source to examine.
* @return a validated lightning source. If null, return
* {@link LightningConstants#DEFAULT_SOURCE}. If empty, return
* {@link LightningConstants#UNKNOWN_SOURCE}. If neither, return the
* original input.
*/
public static String validateSource(String iSource) {
if (iSource == null) {
return LightningConstants.DEFAULT_SOURCE; return LightningConstants.DEFAULT_SOURCE;
} else if (strike.getLightSource().isEmpty()) { } else if (iSource.isEmpty()) {
return LightningConstants.UNKNOWN_SOURCE; return LightningConstants.UNKNOWN_SOURCE;
} else { } else {
return strike.getLightSource(); return iSource;
} }
} }