From 0037b2b7f6c41621aab39eb881ec6d8e5825e73f Mon Sep 17 00:00:00 2001 From: Jonathan Sanchez Date: Mon, 1 Oct 2012 12:19:32 -0500 Subject: [PATCH] Issue #1149 Refactored watches and warnings. Former-commit-id: d0ce171ca5a4568fb1e90b3ca5436e20c5fc4e79 [formerly b19f4810e200ac2b7470f5be3bc7659e651a2cd8] [formerly e859dd05e94f5c45923c0ab5821017085f7a0922 [formerly 3e4148ce433d9f8810f383f56c8ca9b24802de93]] Former-commit-id: e859dd05e94f5c45923c0ab5821017085f7a0922 Former-commit-id: 81853ab4ab118cca13642b83e09dae1d9dcf8ef2 --- .../viz/warnings/rsc/AbstractWWAResource.java | 501 +++++++++++- .../warnings/rsc/AbstractWarningResource.java | 763 ------------------ .../warnings/rsc/AbstractWatchesResource.java | 539 ------------- .../viz/warnings/rsc/CWASPSResource.java | 13 +- .../warnings/rsc/WarningRecordComparator.java | 24 +- .../viz/warnings/rsc/WarningsResource.java | 399 ++++++--- .../viz/warnings/rsc/WatchesResource.java | 500 ++++++------ 7 files changed, 1087 insertions(+), 1652 deletions(-) delete mode 100644 cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWarningResource.java delete mode 100644 cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWatchesResource.java diff --git a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWWAResource.java b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWWAResource.java index 266fb382aa..cf5ea38296 100644 --- a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWWAResource.java +++ b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWWAResource.java @@ -2,19 +2,59 @@ package com.raytheon.viz.warnings.rsc; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.swt.graphics.RGB; +import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.raytheon.uf.common.dataplugin.PluginDataObject; import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord; import com.raytheon.uf.common.dataplugin.warning.PracticeWarningRecord; +import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction; +import com.raytheon.uf.common.dataquery.requests.RequestConstraint; +import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType; +import com.raytheon.uf.common.geospatial.ReferencedCoordinate; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.time.DataTime; +import com.raytheon.uf.common.time.SimulatedTime; +import com.raytheon.uf.common.time.TimeRange; +import com.raytheon.uf.viz.core.DrawableString; +import com.raytheon.uf.viz.core.IGraphicsTarget; +import com.raytheon.uf.viz.core.IGraphicsTarget.HorizontalAlignment; +import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle; +import com.raytheon.uf.viz.core.IGraphicsTarget.TextStyle; +import com.raytheon.uf.viz.core.IGraphicsTarget.VerticalAlignment; +import com.raytheon.uf.viz.core.VizApp; +import com.raytheon.uf.viz.core.catalog.LayerProperty; +import com.raytheon.uf.viz.core.datastructure.DataCubeContainer; +import com.raytheon.uf.viz.core.drawables.IDescriptor.FramesInfo; +import com.raytheon.uf.viz.core.drawables.IFont; +import com.raytheon.uf.viz.core.drawables.IShadedShape; +import com.raytheon.uf.viz.core.drawables.IWireframeShape; +import com.raytheon.uf.viz.core.drawables.PaintProperties; import com.raytheon.uf.viz.core.exception.VizException; import com.raytheon.uf.viz.core.map.MapDescriptor; import com.raytheon.uf.viz.core.rsc.AbstractVizResource; import com.raytheon.uf.viz.core.rsc.IResourceDataChanged; import com.raytheon.uf.viz.core.rsc.LoadProperties; +import com.raytheon.uf.viz.core.rsc.ResourceType; +import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability; +import com.raytheon.uf.viz.core.rsc.capabilities.MagnificationCapability; +import com.raytheon.uf.viz.core.rsc.capabilities.OutlineCapability; import com.raytheon.viz.core.mode.CAVEMode; import com.raytheon.viz.warnings.DateUtil; +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.Point; +import com.vividsolutions.jts.geom.prep.PreparedGeometry; +import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory; /** * @@ -33,6 +73,7 @@ import com.raytheon.viz.warnings.DateUtil; * May 31, 2012 DR14992 mgamazaychikov Changed the order of strings in the * String array returned from getText method * Jun 04, 2012 DR14992 mgamazaychikov Reversed the previous changes + * Sep 26, 2012 jsanchez Refactored AbstractWarningResource and AbstractWatchesResource into this class. * * * @@ -42,6 +83,60 @@ import com.raytheon.viz.warnings.DateUtil; public abstract class AbstractWWAResource extends AbstractVizResource implements IResourceDataChanged { + protected static final transient IUFStatusHandler statusHandler = UFStatus + .getHandler(AbstractWWAResource.class); + + protected class WarningEntry { + + protected AbstractWarningRecord record; + + protected IWireframeShape wireframeShape; + + protected IShadedShape shadedShape; + + /** + * whether or not the warning has been altered, ie CON, CAN, EXP. a + * warning can only be altered once with the exception of a partial + * cancel. + **/ + protected boolean altered = false; + + protected Date timeAltered; + + protected Date frameAltered; + + /** + * was the alter a partial cancel? if it was then a matching CON should + * be processed and added + */ + protected boolean partialCancel = false; + + /** + * set to true if paint needs to re-init the shape + */ + protected boolean project = false; + + } + + protected static GeometryFactory gf = new GeometryFactory(); + + protected static PreparedGeometryFactory pgf = new PreparedGeometryFactory(); + + /** one hour ahead, entirely arbitrary/magic **/ + private static final long LAST_FRAME_ADJ = (60 * 60 * 1000); + + protected String resourceName; + + /** map of dataURI to a warning entry **/ + protected Map entryMap; + + protected IFont warningsFont; + + protected RGB color; + + protected DataTime earliestRequested; + + protected final Object paintLock = new Object(); private static final DataTime[] dataTimes = AbstractVizResource.TIME_AGNOSTIC .toArray(new DataTime[0]); @@ -57,13 +152,23 @@ public abstract class AbstractWWAResource extends protected List recordsToLoad; + protected WarningRecordComparator comparator = new WarningRecordComparator(); + + protected abstract void updateDisplay(IGraphicsTarget target) + throws VizException; + + protected abstract void initShape(IGraphicsTarget target, + AbstractWarningRecord record) throws VizException; + public AbstractWWAResource(WWAResourceData data, LoadProperties props) { super(data, props); this.recordsToLoad = new ArrayList(); + resourceData.addChangeListener(this); + getCapability(OutlineCapability.class).setOutlineWidth(2); + color = getCapability((ColorableCapability.class)).getColor(); + this.entryMap = new ConcurrentHashMap(); } - protected final WarningRecordComparator comparator = new WarningRecordComparator(); - /* * (non-Javadoc) * @@ -79,6 +184,316 @@ public abstract class AbstractWWAResource extends return true; } + @Override + public String inspect(ReferencedCoordinate coord) throws VizException { + if (resourceData.hideSampling) { + return ""; + } + // check if we are in the last frame + boolean lastFrame = false; + FramesInfo framesInfo = this.descriptor.getFramesInfo(); + int frameIdx = framesInfo.getFrameIndex(); + DataTime[] frameTimes = framesInfo.getFrameTimes(); + if (frameIdx < 0 || frameIdx >= frameTimes.length) + return "NO DATA"; + DataTime time = frameTimes[frameIdx]; + + TimeRange framePeriod = null; + if (frameIdx + 1 < frameTimes.length) { + framePeriod = new TimeRange(time.getRefTime(), + frameTimes[frameIdx + 1].getRefTime()); + } else { + framePeriod = new TimeRange(time.getRefTime(), LAST_FRAME_ADJ); + lastFrame = true; + } + + if (time != null) { + try { + Point point = gf.createPoint(coord.asLatLon()); + + for (String key : entryMap.keySet()) { + + WarningEntry entry = entryMap.get(key); + AbstractWarningRecord record = entry.record; + if (matchesFrame(entry, time, framePeriod, lastFrame)) { + + Geometry recordGeom = record.getGeometry(); + for (int i = 0; i < recordGeom.getNumGeometries(); i++) { + PreparedGeometry prepGeom = pgf.create(recordGeom + .getGeometryN(i)); + + if (prepGeom.contains(point)) { + StringBuffer sb = new StringBuffer(); + String[] textToPrint = getText(record, 0); + for (String text : textToPrint) { + if (sb.length() > 0) { + sb.append(" "); + } + sb.append(text); + } + return sb.toString(); + } + } + } + } + } catch (Exception e) { + throw new VizException("Error inspecting resource", e); + } + + } + return "NO DATA"; + } + + protected void disposeEntry(final WarningEntry entry) { + if (entry.wireframeShape != null || entry.shadedShape != null) { + VizApp.runAsync(new Runnable() { + @Override + public void run() { + if (entry.shadedShape != null) { + entry.shadedShape.dispose(); + } + if (entry.wireframeShape != null) { + entry.wireframeShape.dispose(); + } + } + }); + } + } + + @Override + public void project(CoordinateReferenceSystem crs) throws VizException { + synchronized (paintLock) { + for (Map.Entry entry : entryMap.entrySet()) { + WarningEntry warning = entry.getValue(); + // dispose and set to null just to be safe + if (warning.shadedShape != null) { + warning.shadedShape.dispose(); + warning.shadedShape = null; + } + if (warning.wireframeShape != null) { + warning.wireframeShape.dispose(); + warning.wireframeShape = null; + } + warning.project = true; + } + } + } + + @Override + protected void paintInternal(IGraphicsTarget target, + PaintProperties paintProps) throws VizException { + FramesInfo info = paintProps.getFramesInfo(); + DataTime[] frames = info.getFrameTimes(); + int frameToRequestedCompare = frames[0].compareTo(earliestRequested); + if (frameToRequestedCompare < 0) { + // we haven't requested data this far back + this.requestData(frames[0]); + } else if (frameToRequestedCompare > 0) { + // the previous earliest frame was removed as updates came in, so + // the warnings need to be disposed and we need to update the + // earliestRequested so if they ever went back in time to that + // again, it would be re-requested + earliestRequested = frames[0]; + if (paintProps.getDataTime() != null) { + cleanupData(paintProps.getDataTime(), frames); + } + } + int index = info.getFrameIndex(); + if (!this.recordsToLoad.isEmpty()) { + this.updateDisplay(target); + } + + DataTime thisFrameTime = null; + if (index > -1 && index < frames.length) { + thisFrameTime = frames[index]; + } + if (thisFrameTime == null) { + return; + } + + TimeRange framePeriod = null; + boolean lastFrame = false; + if (index + 1 < frames.length) { + framePeriod = new TimeRange(thisFrameTime.getRefTime(), + frames[index + 1].getRefTime()); + } else { + framePeriod = new TimeRange(thisFrameTime.getRefTime(), + LAST_FRAME_ADJ); + lastFrame = true; + } + synchronized (paintLock) { + HashMap candidates = new HashMap(); + for (WarningEntry entry : entryMap.values()) { + if (matchesFrame(entry, paintProps.getDataTime(), framePeriod, + lastFrame)) { + String key = getEventKey(entry); + WarningEntry current = candidates.get(key); + + if (current == null + || current.record.getIssueTime().before( + entry.record.getIssueTime()) + || (current.record.getIssueTime().equals( + entry.record.getIssueTime()) && current.record + .getInsertTime().before( + entry.record.getInsertTime()))) + candidates.put(key, entry); + } + } + for (WarningEntry entry : candidates.values()) { + AbstractWarningRecord record = entry.record; + + // check shapes + if (entry.project) { + initShape(target, entry.record); + entry.project = false; + } + + if (entry != null && entry.wireframeShape != null) { + LineStyle lineStyle = (record.getProductClass() != null && record + .getProductClass().equals("T")) ? LineStyle.DASHED + : LineStyle.SOLID; + target.drawWireframeShape( + entry.wireframeShape, + getCapability(ColorableCapability.class).getColor(), + getCapability(OutlineCapability.class) + .getOutlineWidth(), lineStyle); + } else if (entry != null && entry.shadedShape != null) { + target.drawShadedShape(entry.shadedShape, 1); + } + + if (record != null && record.getGeometry() != null) { + // Calculate the upper left portion of the polygon + Coordinate upperLeft = new Coordinate(180, -90); + + for (Coordinate c : record.getGeometry().getCoordinates()) { + if (c.y - c.x > upperLeft.y - upperLeft.x) { + upperLeft = c; + } + } + + double[] d = descriptor.worldToPixel(new double[] { + upperLeft.x, upperLeft.y }); + d[0] -= paintProps.getZoomLevel() * 100; + + double mapWidth = descriptor.getMapWidth() + * paintProps.getZoomLevel() / 1000; + String[] textToPrint = getText(record, mapWidth); + if (warningsFont == null) { + warningsFont = target.getDefaultFont().deriveWithSize( + 11); + } + // DR14992: reverse the textToPrint array to plot the + // strings in correct order + String[] textToPrintReversed = new String[textToPrint.length]; + for (int i = 0; i < textToPrint.length; i++) { + textToPrintReversed[i] = textToPrint[textToPrint.length + - i - 1]; + } + + DrawableString params = new DrawableString( + textToPrintReversed, color); + params.font = warningsFont; + params.setCoordinates(d[0], d[1]); + params.textStyle = TextStyle.NORMAL; + params.horizontalAlignment = HorizontalAlignment.RIGHT; + params.verticallAlignment = VerticalAlignment.BOTTOM; + params.magnification = getCapability( + MagnificationCapability.class).getMagnification(); + target.drawStrings(params); + } + } + } + } + + abstract protected String getEventKey(WarningEntry entry); + + protected boolean matchesFrame(WarningEntry entry, DataTime paintTime, + TimeRange framePeriod, boolean lastFrame) { + TimeRange recordPeriod = new TimeRange(entry.record.getStartTime() + .getTimeInMillis(), entry.record.getEndTime().getTimeInMillis()); + long diff = entry.record.getEndTime().getTimeInMillis() + - entry.record.getStartTime().getTimeInMillis(); + Date centerTime = new Date(entry.record.getStartTime() + .getTimeInMillis() + (diff / 2)); + Date frameTime = framePeriod.getStart(); + + Date frameStart = framePeriod.getStart(); + Date refTime = entry.record.getDataTime().getRefTime(); + + if (lastFrame) { + // use current system time to determine what to display + Date timeToDisplay = SimulatedTime.getSystemTime().getTime(); + // change frame time + frameTime = timeToDisplay; + // point paint time to different time + paintTime = new DataTime(timeToDisplay); + // point framePeriod to new frame + framePeriod = new TimeRange(frameTime, LAST_FRAME_ADJ); + } + + // check if the warning is cancelled + WarningAction action = WarningAction.valueOf(entry.record.getAct()); + if (action == WarningAction.CAN && refTime.equals(paintTime)) { + return false; + // If this entry has been altered/updated, display its pre-altered + // version + // only in the frames prior to the time it was altered + } else if (entry.altered) { + if (frameStart.getTime() >= refTime.getTime() + && frameStart.getTime() < (entry.timeAltered.getTime()) + && frameStart.getTime() < entry.frameAltered.getTime()) + return true; + if (frameStart.getTime() >= (entry.timeAltered.getTime())) + return false; + + } else if (refTime.equals(paintTime) + || recordPeriod.contains(frameTime) + || (framePeriod.contains(centerTime) && (!lastFrame || !entry.altered))) { + return true; + } + + return false; + } + + protected void cleanupData(DataTime paintTime, DataTime[] descFrameTimes) { + System.out.println("entryMap size " + entryMap.size()); + List framePeriods = new ArrayList( + descFrameTimes.length); + for (int i = 0; i < descFrameTimes.length; i++) { + if (i == descFrameTimes.length - 1) { + framePeriods.add(new TimeRange(descFrameTimes[i].getRefTime(), + LAST_FRAME_ADJ)); + } else { + framePeriods.add(new TimeRange(descFrameTimes[i].getRefTime(), + descFrameTimes[i + 1].getRefTime())); + } + } + + int size = framePeriods.size(); + List toRemove = new ArrayList(); + for (String key : entryMap.keySet()) { + WarningEntry entry = entryMap.get(key); + boolean found = false; + for (int i = 0; i < size; i++) { + TimeRange tr = framePeriods.get(i); + if (matchesFrame(entry, paintTime, tr, (i == size - 1))) { + found = true; + break; + } + } + + if (!found) { + toRemove.add(key); + } + } + + for (String key : toRemove) { + WarningEntry entry = entryMap.remove(key); + System.out.println("removing " + entry.record.getDataURI()); + disposeEntry(entry); + } + } + public synchronized void addRecord(PluginDataObject[] pdos) throws VizException { for (PluginDataObject pdo : pdos) { @@ -94,6 +509,59 @@ public abstract class AbstractWWAResource extends } } + protected PluginDataObject[] sort(PluginDataObject[] pdos) { + ArrayList sortedWarnings = new ArrayList(); + for (Object o : pdos) { + if (((PluginDataObject) o) instanceof AbstractWarningRecord) { + AbstractWarningRecord record = (AbstractWarningRecord) o; + sortedWarnings.add(record); + } + } + + /* Sorts by phensig, etn, starttime (descending), act */ + Collections.sort(sortedWarnings, comparator); + return sortedWarnings.toArray(new AbstractWarningRecord[sortedWarnings + .size()]); + } + + @SuppressWarnings("unchecked") + protected void requestData(DataTime earliest) throws VizException { + System.out.println("requesting data"); + Map map = (Map) resourceData + .getMetadataMap().clone(); + if (earliestRequested != null) { + // don't request data we've already requested + String[] times = new String[] { earliest.toString(), + earliestRequested.toString() }; + RequestConstraint constraint = new RequestConstraint(); + constraint.setConstraintType(ConstraintType.BETWEEN); + constraint.setBetweenValueList(times); + map.put("endTime", constraint); + } else { + RequestConstraint endConstraint = new RequestConstraint( + earliest.toString(), ConstraintType.GREATER_THAN_EQUALS); + map.put("endTime", endConstraint); + } + + earliestRequested = earliest; + + LayerProperty property = new LayerProperty(); + property.setDesiredProduct(ResourceType.PLAN_VIEW); + property.setEntryQueryParameters(map, false); + property.setNumberOfImages(9999); + + Object[] resp = null; + resp = DataCubeContainer.getData(property, 60000).toArray( + new Object[] {}); + PluginDataObject[] arr = new PluginDataObject[resp.length]; + int i = 0; + for (Object o : resp) { + arr[i] = (PluginDataObject) o; + i++; + } + addRecord(sort(arr)); + } + protected String[] getText(AbstractWarningRecord record, double mapWidth) { String vid = record.getPhensig(); String phen = record.getPhen(); @@ -147,4 +615,33 @@ public abstract class AbstractWWAResource extends } } + @Override + public String getName() { + String name = resourceData.name != null ? resourceData.name + : resourceName; + + DataTime[] times = this.descriptor.getFramesInfo().getFrameTimes(); + int timeIdx = this.descriptor.getFramesInfo().getFrameIndex(); + + // handle last frame differently, it should always be the latest time + boolean lastFrame = false; + if (timeIdx == times.length - 1) { + lastFrame = true; + } + DataTime time = null; + + // get time to display + if (lastFrame) { + time = new DataTime(SimulatedTime.getSystemTime().getTime()); + } else if (timeIdx > -1 && timeIdx < times.length) { + time = times[timeIdx]; + } + + // add time to legend + if (time != null) { + name += " " + time.getLegendString(); + } + return name; + } + } diff --git a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWarningResource.java b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWarningResource.java deleted file mode 100644 index c8bfe49884..0000000000 --- a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWarningResource.java +++ /dev/null @@ -1,763 +0,0 @@ -package com.raytheon.viz.warnings.rsc; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.ConcurrentHashMap; - -import org.eclipse.swt.graphics.RGB; -import org.opengis.referencing.crs.CoordinateReferenceSystem; - -import com.raytheon.uf.common.dataplugin.PluginDataObject; -import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord; -import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction; -import com.raytheon.uf.common.geospatial.ReferencedCoordinate; -import com.raytheon.uf.common.status.IUFStatusHandler; -import com.raytheon.uf.common.status.UFStatus; -import com.raytheon.uf.common.status.UFStatus.Priority; -import com.raytheon.uf.common.time.DataTime; -import com.raytheon.uf.common.time.SimulatedTime; -import com.raytheon.uf.common.time.TimeRange; -import com.raytheon.uf.viz.core.DrawableString; -import com.raytheon.uf.viz.core.IGraphicsTarget; -import com.raytheon.uf.viz.core.IGraphicsTarget.HorizontalAlignment; -import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle; -import com.raytheon.uf.viz.core.IGraphicsTarget.TextStyle; -import com.raytheon.uf.viz.core.IGraphicsTarget.VerticalAlignment; -import com.raytheon.uf.viz.core.VizApp; -import com.raytheon.uf.viz.core.drawables.IDescriptor.FramesInfo; -import com.raytheon.uf.viz.core.drawables.IFont; -import com.raytheon.uf.viz.core.drawables.IRenderableDisplay; -import com.raytheon.uf.viz.core.drawables.IShadedShape; -import com.raytheon.uf.viz.core.drawables.IWireframeShape; -import com.raytheon.uf.viz.core.drawables.PaintProperties; -import com.raytheon.uf.viz.core.exception.VizException; -import com.raytheon.uf.viz.core.rsc.AbstractVizResource; -import com.raytheon.uf.viz.core.rsc.IResourceDataChanged; -import com.raytheon.uf.viz.core.rsc.LoadProperties; -import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability; -import com.raytheon.uf.viz.core.rsc.capabilities.MagnificationCapability; -import com.raytheon.uf.viz.core.rsc.capabilities.OutlineCapability; -import com.vividsolutions.jts.geom.Coordinate; -import com.vividsolutions.jts.geom.Geometry; -import com.vividsolutions.jts.geom.GeometryFactory; -import com.vividsolutions.jts.geom.Point; -import com.vividsolutions.jts.geom.prep.PreparedGeometry; -import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory; - -/** - * - * TODO njensen: This was a refactored version of AbstractWWAResource. The - * WatchesResource was not refactored to work with it. Ideally this class and - * AbstractWWAResource should be combined so it supports both watches and - * warnings. - * - *
- * 
- * SOFTWARE HISTORY
- * 
- * Date         Ticket#    Engineer    Description
- * ------------ ---------- ----------- --------------------------
- * May 3, 2011            jsanchez     Initial creation
- * Aug 5, 2011            njensen       Refactored maps
- * Aug 22, 2011  10631   njensen  Major refactor
- * 2012-04-16   DR 14866   D. Friedman Fix sampling error
- * May 3, 2012  DR 14741  porricel      Updated matchesFrame function
- *                                      to make SVS warning updates and
- *                                      original warning display properly
- *                                      in a given display frame
- * Jun 04, 2012 DR14992  mgamazaychikov Reversed the textToPrint array to
- * 										plot the strings in correct order
- * Aug 09, 2012 DR 15166  D.Friedman    Plot only the most recent record for
- *                                      an event.
- * 
- * - * @author jsanchez - * @version 1.0 - */ -public abstract class AbstractWarningResource extends AbstractWWAResource - implements IResourceDataChanged { - private static final transient IUFStatusHandler statusHandler = UFStatus - .getHandler(AbstractWarningResource.class); - - private static GeometryFactory gf = new GeometryFactory(); - - private static PreparedGeometryFactory pgf = new PreparedGeometryFactory(); - - protected static class RepaintHeartbeat extends TimerTask { - - private HashSet> resourceSet = new HashSet>(); - - private boolean cancelled = false; - - public RepaintHeartbeat() { - - } - - /** - * copy resources from old task, just in case some were added after it - * should have been replaced (threads are fun) - **/ - public void copyResourceSet(RepaintHeartbeat oldTask) { - // copy resources, in case one was added after a cancel - Set> oldResourceSet = oldTask - .getResourceSet(); - synchronized (oldResourceSet) { - for (AbstractVizResource rsc : oldResourceSet) { - this.addResource(rsc); - } - } - } - - public Set> getResourceSet() { - return resourceSet; - } - - @Override - public void run() { - // get the unique displays from all the added resources - ArrayList displaysToRefresh = new ArrayList( - 1); - synchronized (resourceSet) { - for (AbstractVizResource rsc : resourceSet) { - try { - IRenderableDisplay disp = rsc.getDescriptor() - .getRenderableDisplay(); - if (!displaysToRefresh.contains(disp)) { - displaysToRefresh.add(disp); - } - } catch (Exception e) { - statusHandler - .handle(Priority.PROBLEM, - "Encountered error during Warnings Heartbeat, continuing with other Warnings ", - e); - } - } - } - - // create an array with final modifier - final IRenderableDisplay[] refreshList = displaysToRefresh - .toArray(new IRenderableDisplay[displaysToRefresh.size()]); - - // execute refersh in UI thread - VizApp.runAsync(new Runnable() { - - @Override - public void run() { - for (IRenderableDisplay disp : refreshList) { - disp.refresh(); - } - } - - }); - - // cancel the task if there are no more resources - boolean cancel = false; - synchronized (resourceSet) { - if (resourceSet.size() < 1) { - cancel = true; - } - } - - if (cancel) { - doCancel(); - } - } - - public void addResource(AbstractVizResource rsc) { - // if task has no resources then it needs to be started when the - // first is added - boolean start = false; - synchronized (resourceSet) { - // if this is the first resource added to an empty set start the - // timer - if (resourceSet.size() < 1) { - start = true; - } - resourceSet.add(rsc); - } - if (start) { - AbstractWarningResource.scheduleHeartBeat(); - } - } - - public void removeResource(AbstractVizResource rsc) { - synchronized (resourceSet) { - resourceSet.remove(rsc); - // cancel the task if there are no more resources - if (resourceSet.size() < 1) { - doCancel(); - } - } - } - - private void doCancel() { - synchronized (heartBeatChangeLock) { - if (cancelled == false) { - cancelled = true; - heartBeatTimer.cancel(); - heartBeatTask = new RepaintHeartbeat(); - heartBeatTimer = new Timer(); - heartBeatTask.copyResourceSet(this); - } - } - } - } - - /** lock when changing heartBeatTask **/ - protected static final Object heartBeatChangeLock = new Object(); - - protected static RepaintHeartbeat heartBeatTask = null; - - protected static Timer heartBeatTimer = null; - - /** one hour ahead, entirely arbitrary/magic **/ - private static final long LAST_FRAME_ADJ = (60 * 60 * 1000); - - protected class WarningEntry { - - protected AbstractWarningRecord record; - - protected IWireframeShape wireframeShape; - - protected IShadedShape shadedShape; - - /** - * whether or not the warning has been altered, ie CON, CAN, EXP. a - * warning can only be altered once with the exception of a partial - * cancel. - **/ - protected boolean altered = false; - - protected Date timeAltered; - - protected Date frameAltered; - - /** - * was the alter a partial cancel? if it was then a matching CON should - * be processed and added - */ - protected boolean partialCancel = false; - - /** - * set to true if paint needs to re-init the shape - */ - protected boolean project = false; - - } - - /** map of dataURI to a warning entry **/ - protected Map entryMap; - - protected IFont warningsFont; - - protected RGB color; - - protected DataTime earliestRequested; - - protected final Object paintLock = new Object(); - - /** - * Constructor - */ - public AbstractWarningResource(WWAResourceData data, LoadProperties props) { - super(data, props); - resourceData.addChangeListener(this); - getCapability(OutlineCapability.class).setOutlineWidth(2); - color = getCapability((ColorableCapability.class)).getColor(); - this.entryMap = new ConcurrentHashMap(); - } - - /** - * schedule the heart beat for the next minute - */ - protected static void scheduleHeartBeat() { - // get simulated time - Date currentTime = SimulatedTime.getSystemTime().getTime(); - // get a calendar - Calendar now = Calendar.getInstance(); - // set calendar time to simulated time - now.setTime(currentTime); - // add one to the minutes field - now.add(Calendar.MINUTE, 1); - // reset second and milisecond to 0 - now.set(Calendar.SECOND, 0); - now.set(Calendar.MILLISECOND, 0); - // schedule task to fire every minute - synchronized (heartBeatChangeLock) { - try { - if (heartBeatTimer == null) { - heartBeatTimer = new Timer(); - } - // schedule on the minute every minute - heartBeatTimer.schedule(heartBeatTask, now.getTime(), - 1 * 60 * 1000); - } catch (Exception e) { - try { - heartBeatTimer.cancel(); - } catch (Exception e2) { - // ignore, we just want to make sure the timer is cancelled - } finally { - // create a new task if there was an error when scheduling - heartBeatTask = new RepaintHeartbeat(); - heartBeatTimer = new Timer(); - } - statusHandler.handle(Priority.SIGNIFICANT, - "Error scheduling warnings heart beat ", e); - } - } - } - - @Override - protected void initInternal(IGraphicsTarget target) throws VizException { - synchronized (heartBeatChangeLock) { - if (heartBeatTask == null) { - heartBeatTask = new RepaintHeartbeat(); - } - heartBeatTask.addResource(this); - } - synchronized (this) { - try { - addRecord(getWarningRecordArray()); - } catch (VizException e) { - e.printStackTrace(); - } - } - } - - /* - * (non-Javadoc) - * - * @see com.raytheon.viz.core.rsc.IVizResource#dispose() - */ - @Override - protected void disposeInternal() { - synchronized (heartBeatChangeLock) { - heartBeatTask.removeResource(this); - } - for (WarningEntry entry : entryMap.values()) { - if (entry.shadedShape != null) { - entry.shadedShape.dispose(); - } - if (entry.wireframeShape != null) { - entry.wireframeShape.dispose(); - } - } - - entryMap.clear(); - if (warningsFont != null) { - warningsFont.dispose(); - } - } - - @Override - public String inspect(ReferencedCoordinate coord) throws VizException { - if (resourceData.hideSampling) { - return ""; - } - // check if we are in the last frame - boolean lastFrame = false; - FramesInfo framesInfo = this.descriptor.getFramesInfo(); - int frameIdx = framesInfo.getFrameIndex(); - DataTime[] frameTimes = framesInfo.getFrameTimes(); - if (frameIdx < 0 || frameIdx >= frameTimes.length) - return "NO DATA"; - DataTime time = frameTimes[frameIdx]; - - TimeRange framePeriod = null; - if (frameIdx + 1 < frameTimes.length) { - framePeriod = new TimeRange(time.getRefTime(), - frameTimes[frameIdx + 1].getRefTime()); - } else { - framePeriod = new TimeRange(time.getRefTime(), LAST_FRAME_ADJ); - lastFrame = true; - } - - if (time != null) { - try { - Point point = gf.createPoint(coord.asLatLon()); - - for (String key : entryMap.keySet()) { - - WarningEntry entry = entryMap.get(key); - AbstractWarningRecord record = entry.record; - if (matchesFrame(entry, time, framePeriod, lastFrame)) { - - Geometry recordGeom = record.getGeometry(); - for (int i = 0; i < recordGeom.getNumGeometries(); i++) { - PreparedGeometry prepGeom = pgf.create(recordGeom - .getGeometryN(i)); - - if (prepGeom.contains(point)) { - StringBuffer sb = new StringBuffer(); - String[] textToPrint = getText(record, 0); - for (String text : textToPrint) { - if (sb.length() > 0) { - sb.append(" "); - } - sb.append(text); - } - return sb.toString(); - } - } - } - } - } catch (Exception e) { - throw new VizException("Error inspecting Warning Resource", e); - } - - } - return "NO DATA"; - } - - protected void disposeEntry(final WarningEntry entry) { - if (entry.wireframeShape != null || entry.shadedShape != null) { - VizApp.runAsync(new Runnable() { - @Override - public void run() { - if (entry.shadedShape != null) { - entry.shadedShape.dispose(); - } - if (entry.wireframeShape != null) { - entry.wireframeShape.dispose(); - } - } - }); - } - } - - @Override - public void project(CoordinateReferenceSystem crs) throws VizException { - synchronized (paintLock) { - for (Map.Entry entry : entryMap.entrySet()) { - WarningEntry warning = entry.getValue(); - // dispose and set to null just to be safe - if (warning.shadedShape != null) { - warning.shadedShape.dispose(); - warning.shadedShape = null; - } - if (warning.wireframeShape != null) { - warning.wireframeShape.dispose(); - warning.wireframeShape = null; - } - warning.project = true; - } - } - } - - @Override - public void resourceChanged(ChangeType type, Object object) { - if (type == ChangeType.DATA_UPDATE) { - PluginDataObject[] pdo = (PluginDataObject[]) object; - synchronized (AbstractWarningResource.this) { - { - try { - addRecord(pdo); - } catch (VizException e) { - statusHandler.handle(Priority.SIGNIFICANT, - e.getLocalizedMessage(), e); - } - } - } - } else if (type == ChangeType.CAPABILITY) { - if (color != null - && color.equals(getCapability((ColorableCapability.class)) - .getColor()) == false) { - color = getCapability((ColorableCapability.class)).getColor(); - - // TODO this needs to be fixed to work with watches which are - // shaded - // for (String dataUri : entryMap.keySet()) { - // WarningEntry entry = entryMap.get(dataUri); - // TODO init a shape somewhere else - // if (entry.shadedShape != null) { - // entry.shadedShape.dispose(); - // try { - // initShape(entry.record); - // } catch (VizException e) { - // statusHandler.handle(Priority.PROBLEM, - // e.getLocalizedMessage(), e); - // } - // } - // } - } - } - issueRefresh(); - } - - protected void paintInternal(IGraphicsTarget target, - PaintProperties paintProps) throws VizException { - FramesInfo info = paintProps.getFramesInfo(); - DataTime[] frames = info.getFrameTimes(); - int frameToRequestedCompare = frames[0].compareTo(earliestRequested); - if (frameToRequestedCompare < 0) { - // we haven't requested data this far back - this.requestData(frames[0]); - } else if (frameToRequestedCompare > 0) { - // the previous earliest frame was removed as updates came in, so - // the warnings need to be disposed and we need to update the - // earliestRequested so if they ever went back in time to that - // again, it would be re-requested - earliestRequested = frames[0]; - if (paintProps.getDataTime() != null) { - cleanupData(paintProps.getDataTime(), frames); - } - } - int index = info.getFrameIndex(); - if (!this.recordsToLoad.isEmpty()) { - this.updateDisplay(target); - } - - DataTime thisFrameTime = null; - if (index > -1 && index < frames.length) { - thisFrameTime = frames[index]; - } - if (thisFrameTime == null) { - return; - } - - TimeRange framePeriod = null; - boolean lastFrame = false; - if (index + 1 < frames.length) { - framePeriod = new TimeRange(thisFrameTime.getRefTime(), - frames[index + 1].getRefTime()); - } else { - framePeriod = new TimeRange(thisFrameTime.getRefTime(), - LAST_FRAME_ADJ); - lastFrame = true; - } - synchronized (paintLock) { - HashMap candidates = - new HashMap(); - for (WarningEntry entry : entryMap.values()) { - if (matchesFrame(entry, paintProps.getDataTime(), framePeriod, - lastFrame)) { - String key = getEventKey(entry); - WarningEntry current = candidates.get(key); - - if (current == null || current.record.getIssueTime(). - before(entry.record.getIssueTime()) || - (current.record.getIssueTime().equals(entry.record.getIssueTime()) && - current.record.getInsertTime().before(entry.record.getInsertTime())) - ) - candidates.put(key, entry); - } - } - for (WarningEntry entry : candidates.values()) { - AbstractWarningRecord record = entry.record; - - // check shapes - if (entry.project) { - initShape(target, entry.record); - entry.project = false; - } - - if (entry != null && entry.wireframeShape != null) { - LineStyle lineStyle = (record.getProductClass() != null && record - .getProductClass().equals("T")) ? LineStyle.DASHED - : LineStyle.SOLID; - target.drawWireframeShape(entry.wireframeShape, - getCapability(ColorableCapability.class) - .getColor(), - getCapability(OutlineCapability.class) - .getOutlineWidth(), lineStyle); - } else if (entry != null && entry.shadedShape != null) { - target.drawShadedShape(entry.shadedShape, 1); - } - - if (record != null && record.getGeometry() != null) { - // Calculate the upper left portion of the polygon - Coordinate upperLeft = new Coordinate(180, -90); - - for (Coordinate c : record.getGeometry() - .getCoordinates()) { - if (c.y - c.x > upperLeft.y - upperLeft.x) { - upperLeft = c; - } - } - - double[] d = descriptor.worldToPixel(new double[] { - upperLeft.x, upperLeft.y }); - d[0] -= paintProps.getZoomLevel() * 100; - - double mapWidth = descriptor.getMapWidth() - * paintProps.getZoomLevel() / 1000; - String[] textToPrint = getText(record, mapWidth); - if (warningsFont == null) { - warningsFont = target.getDefaultFont() - .deriveWithSize(11); - } - // DR14992: reverse the textToPrint array to plot the strings in correct order - String [] textToPrintReversed = new String[textToPrint.length]; - for(int i = 0; i < textToPrint.length; i++) { - textToPrintReversed[i] = textToPrint[textToPrint.length - - i - 1]; - } - - DrawableString params = new DrawableString(textToPrintReversed, - color); - params.font = warningsFont; - params.setCoordinates(d[0], d[1]); - params.textStyle = TextStyle.NORMAL; - params.horizontalAlignment = HorizontalAlignment.RIGHT; - params.verticallAlignment = VerticalAlignment.BOTTOM; - params.magnification = getCapability( - MagnificationCapability.class) - .getMagnification(); - target.drawStrings(params); - } - } - } - } - - private static String getEventKey(WarningEntry entry) { - AbstractWarningRecord r = entry.record; - return r.getOfficeid() + '.' + r.getPhensig() + '.' + r.getEtn(); - } - - protected boolean matchesFrame(WarningEntry entry, DataTime paintTime, - TimeRange framePeriod, boolean lastFrame) { - TimeRange recordPeriod = new TimeRange(entry.record.getStartTime() - .getTimeInMillis(), entry.record.getEndTime().getTimeInMillis()); - long diff = entry.record.getEndTime().getTimeInMillis() - - entry.record.getStartTime().getTimeInMillis(); - Date centerTime = new Date(entry.record.getStartTime() - .getTimeInMillis() + (diff / 2)); - Date frameTime = framePeriod.getStart(); - - Date frameStart = framePeriod.getStart(); - Date refTime = entry.record.getDataTime().getRefTime(); - - if (lastFrame) { - // use current system time to determine what to display - Date timeToDisplay = SimulatedTime.getSystemTime().getTime(); - // change frame time - frameTime = timeToDisplay; - // point paint time to different time - paintTime = new DataTime(timeToDisplay); - // point framePeriod to new frame - framePeriod = new TimeRange(frameTime, LAST_FRAME_ADJ); - } - - // check if the warning is cancelled - WarningAction action = WarningAction.valueOf(entry.record.getAct()); - if (action == WarningAction.CAN && refTime.equals(paintTime)) { - return false; - // If this entry has been altered/updated, display its pre-altered - // version - // only in the frames prior to the time it was altered - } else if (entry.altered) { - if (frameStart.getTime() >= refTime.getTime() - && frameStart.getTime() < (entry.timeAltered.getTime()) - && frameStart.getTime() < entry.frameAltered.getTime()) - return true; - if (frameStart.getTime() >= (entry.timeAltered.getTime())) - return false; - - } else if (refTime.equals(paintTime) - || recordPeriod.contains(frameTime) - || (framePeriod.contains(centerTime) && (!lastFrame || !entry.altered))) { - return true; - } - - return false; - } - - protected void cleanupData(DataTime paintTime, DataTime[] descFrameTimes) { - System.out.println("entryMap size " + entryMap.size()); - List framePeriods = new ArrayList( - descFrameTimes.length); - for (int i = 0; i < descFrameTimes.length; i++) { - if (i == descFrameTimes.length - 1) { - framePeriods.add(new TimeRange(descFrameTimes[i].getRefTime(), - LAST_FRAME_ADJ)); - } else { - framePeriods.add(new TimeRange(descFrameTimes[i].getRefTime(), - descFrameTimes[i + 1].getRefTime())); - } - } - - int size = framePeriods.size(); - List toRemove = new ArrayList(); - for (String key : entryMap.keySet()) { - WarningEntry entry = entryMap.get(key); - boolean found = false; - for (int i = 0; i < size; i++) { - TimeRange tr = framePeriods.get(i); - if (matchesFrame(entry, paintTime, tr, (i == size - 1))) { - found = true; - break; - } - } - - if (!found) { - toRemove.add(key); - } - } - - for (String key : toRemove) { - WarningEntry entry = entryMap.remove(key); - System.out.println("removing " + entry.record.getDataURI()); - disposeEntry(entry); - } - } - - protected abstract void updateDisplay(IGraphicsTarget target); - - protected abstract void requestData(DataTime earliest) throws VizException; - - protected abstract void initShape(IGraphicsTarget target, - AbstractWarningRecord record); - - public synchronized void addRecord(PluginDataObject[] pdos) - throws VizException { - for (PluginDataObject pdo : pdos) { - if (pdo instanceof AbstractWarningRecord) { - AbstractWarningRecord record = (AbstractWarningRecord) pdo; - String officeid = record.getOfficeid(); - if (!resourceData.getMetadataMap().containsKey("officeid") - || resourceData.getMetadataMap().get("officeid") - .getConstraintValue().contains(officeid)) { - this.recordsToLoad.add((AbstractWarningRecord) pdo); - } - } - } - } - - @Override - public String getName() { - String name = resourceData.name != null ? resourceData.name - : "Warnings"; - - DataTime[] times = this.descriptor.getFramesInfo().getFrameTimes(); - int timeIdx = this.descriptor.getFramesInfo().getFrameIndex(); - - // handle last frame differently, it should always be the latest time - boolean lastFrame = false; - if (timeIdx == times.length - 1) { - lastFrame = true; - } - DataTime time = null; - - // get time to display - if (lastFrame) { - time = new DataTime(SimulatedTime.getSystemTime().getTime()); - } else if (timeIdx > -1 && timeIdx < times.length) { - time = times[timeIdx]; - } - - // add time to legend - if (time != null) { - name += " " + time.getLegendString(); - } - return name; - } - -} diff --git a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWatchesResource.java b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWatchesResource.java deleted file mode 100644 index f39dd07db5..0000000000 --- a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/AbstractWatchesResource.java +++ /dev/null @@ -1,539 +0,0 @@ -package com.raytheon.viz.warnings.rsc; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.Timer; -import java.util.TimerTask; - -import org.eclipse.swt.graphics.RGB; -import org.opengis.referencing.crs.CoordinateReferenceSystem; - -import com.raytheon.uf.common.dataplugin.PluginDataObject; -import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord; -import com.raytheon.uf.common.geospatial.ReferencedCoordinate; -import com.raytheon.uf.common.status.IUFStatusHandler; -import com.raytheon.uf.common.status.UFStatus; -import com.raytheon.uf.common.status.UFStatus.Priority; -import com.raytheon.uf.common.time.DataTime; -import com.raytheon.uf.common.time.SimulatedTime; -import com.raytheon.uf.viz.core.DrawableString; -import com.raytheon.uf.viz.core.IGraphicsTarget; -import com.raytheon.uf.viz.core.IGraphicsTarget.HorizontalAlignment; -import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle; -import com.raytheon.uf.viz.core.IGraphicsTarget.TextStyle; -import com.raytheon.uf.viz.core.IGraphicsTarget.VerticalAlignment; -import com.raytheon.uf.viz.core.VizApp; -import com.raytheon.uf.viz.core.drawables.IDescriptor.FramesInfo; -import com.raytheon.uf.viz.core.drawables.IFont; -import com.raytheon.uf.viz.core.drawables.IShadedShape; -import com.raytheon.uf.viz.core.drawables.IWireframeShape; -import com.raytheon.uf.viz.core.drawables.PaintProperties; -import com.raytheon.uf.viz.core.exception.VizException; -import com.raytheon.uf.viz.core.rsc.IResourceDataChanged; -import com.raytheon.uf.viz.core.rsc.LoadProperties; -import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability; -import com.raytheon.uf.viz.core.rsc.capabilities.MagnificationCapability; -import com.raytheon.uf.viz.core.rsc.capabilities.OutlineCapability; -import com.vividsolutions.jts.geom.Coordinate; -import com.vividsolutions.jts.geom.Geometry; -import com.vividsolutions.jts.geom.GeometryFactory; -import com.vividsolutions.jts.geom.Point; -import com.vividsolutions.jts.geom.prep.PreparedGeometry; -import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory; - -/** - * - * TODO njensen: Ideally this should be refactored to be more in line with the - * AbstractWarningsResource. - * - *
- * 
- * SOFTWARE HISTORY
- * 
- * Date         Ticket#    Engineer    Description
- * ------------ ---------- ----------- --------------------------
- * May 3, 2011            jsanchez     Initial creation
- * Aug 5, 2011            njensen       Refactored maps
- * Aug 22, 2011  10631   njensen  Major refactor
- * 
- * 
- * - * @author jsanchez - * @version 1.0 - */ -public abstract class AbstractWatchesResource extends AbstractWWAResource - implements IResourceDataChanged { - private static final transient IUFStatusHandler statusHandler = UFStatus - .getHandler(AbstractWatchesResource.class); - - private static PreparedGeometryFactory pgf = new PreparedGeometryFactory(); - - private static GeometryFactory gf = new GeometryFactory(); - - /** - * - * this task calls redoTimeMatching on the resource, it should be scheduled - * to run for when a warning is set to expire - * - * @author ekladstrup - * @version 1.0 - */ - protected class WarningExpirationTask extends TimerTask { - - private AbstractWatchesResource rsc = null; - - public WarningExpirationTask(AbstractWatchesResource rsc) { - this.rsc = rsc; - } - - @Override - public void run() { - // System.err.println("warning expired"); - // some warning has expired - rsc.redoTimeMatching(this.scheduledExecutionTime()); - rsc.issueRefresh(); - } - - } - - protected class WarningEntry { - - protected AbstractWarningRecord record; - - protected IWireframeShape wireframeShape; - - protected IShadedShape shadedShape; - - protected List times; - } - - protected IGraphicsTarget target; - - /** map of dataURI to a warning entry **/ - protected Map entryMap; - - protected Map> frames; - - protected DataTime displayedDate; - - protected IFont warningsFont; - - protected RGB color; - - private Timer timer; - - private Set expTaskSet; - - /** - * Constructor - */ - public AbstractWatchesResource(WWAResourceData data, LoadProperties props) { - super(data, props); - resourceData.addChangeListener(this); - getCapability(OutlineCapability.class).setOutlineWidth(2); - color = getCapability((ColorableCapability.class)).getColor(); - this.entryMap = new HashMap(); - this.frames = Collections - .synchronizedMap(new HashMap>()); - timer = new Timer(); - expTaskSet = new HashSet(); - } - - @Override - protected void initInternal(IGraphicsTarget target) throws VizException { - if (this.target == null) { - this.target = target; - - synchronized (this) { - try { - addRecord(getWarningRecordArray()); - } catch (VizException e) { - e.printStackTrace(); - } - } - } - // force creation of a frame for any currently active warnings, this - // frame might get displayed in place of the last frame. - initNewFrame(new DataTime(SimulatedTime.getSystemTime().getTime())); - } - - /* - * (non-Javadoc) - * - * @see com.raytheon.viz.core.rsc.IVizResource#dispose() - */ - @Override - protected void disposeInternal() { - timer.cancel(); - - for (WarningEntry entry : entryMap.values()) { - if (entry.shadedShape != null) { - entry.shadedShape.dispose(); - } - if (entry.wireframeShape != null) { - entry.wireframeShape.dispose(); - } - if (entry.times != null) { - entry.times.clear(); - } - } - - entryMap.clear(); - frames.clear(); - if (warningsFont != null) { - warningsFont.dispose(); - } - } - - @Override - public String inspect(ReferencedCoordinate coord) throws VizException { - if (resourceData.hideSampling) { - return ""; - } - - if (this.displayedDate != null - && this.frames.containsKey(displayedDate)) { - - try { - Point point = gf.createPoint(coord.asLatLon()); - - synchronized (frames) { - for (AbstractWarningRecord record : this.frames - .get(displayedDate)) { - - Date entryIssue = record.getStartTime().getTime(); - if (entryIssue == null) { - entryIssue = record.getIssueTime().getTime(); - } - - Date entryEnd = record.getEndTime().getTime(); - Date ref = displayedDate.getRefTime(); - - if (entryIssue.compareTo(ref) <= 0 - && entryEnd.compareTo(ref) >= 0) { - - Geometry recordGeom = record.getGeometry(); - for (int i = 0; i < recordGeom.getNumGeometries(); i++) { - PreparedGeometry prepGeom = pgf - .create(recordGeom.getGeometryN(i)); - - if (prepGeom.contains(point)) { - StringBuffer sb = new StringBuffer(); - String[] textToPrint = getText(record, 0); - - for (String text : textToPrint) { - if (sb.length() > 0) { - sb.append(" "); - } - sb.append(text); - } - - return sb.toString(); - } - } - } - } - } - - } catch (Exception e) { - throw new VizException("Error inspecting Warning Resource", e); - } - } - return "NO DATA"; - } - - protected void removeFrame(DataTime dataTime) { - synchronized (frames) { - DataTime[] frameTimes = descriptor.getFramesInfo().getFrameTimes(); - DataTime lastTime = frameTimes[frameTimes.length - 1]; - if (lastTime != null - && lastTime.getMatchValid() < dataTime.getMatchValid()) { - // Don't remove warnings that are newer than the last frame. - // This allows us to display the latest warnings even if they - // don't time match. These warnings will be cleaned up again on - // the next redoTimeMatching. Hopefully we aren't keeping too - // many around. - return; - } - List warningRecordList = frames - .remove(dataTime); - // remove times can effect stepping through frames - for (AbstractWarningRecord w : warningRecordList) { - final WarningEntry entry = entryMap.get(w.getDataURI()); - if (entry != null) { - List list = entry.times; - if (list != null && list.remove(dataTime)) { - if (list.isEmpty()) { - disposeEntry(entry); - } - } - if (list == null || list.isEmpty()) { - entryMap.remove(w.getDataURI()); - } - } - } - } - - } - - protected void disposeEntry(final WarningEntry entry) { - if (entry.wireframeShape != null || entry.shadedShape != null) { - VizApp.runAsync(new Runnable() { - @Override - public void run() { - if (entry.shadedShape != null) { - entry.shadedShape.dispose(); - } - if (entry.wireframeShape != null) { - entry.wireframeShape.dispose(); - } - } - }); - } - } - - @Override - public void project(CoordinateReferenceSystem crs) throws VizException { - this.frames.clear(); - } - - @Override - public void resourceChanged(ChangeType type, Object object) { - if (type == ChangeType.DATA_UPDATE) { - PluginDataObject[] pdo = (PluginDataObject[]) object; - synchronized (AbstractWatchesResource.this) { - { - try { - addRecord(pdo); - } catch (VizException e) { - statusHandler.handle(Priority.SIGNIFICANT, - e.getLocalizedMessage(), e); - } - } - } - } else if (type == ChangeType.CAPABILITY) { - if (color != null - && color.equals(getCapability((ColorableCapability.class)) - .getColor()) == false) { - color = getCapability((ColorableCapability.class)).getColor(); - - for (String dataUri : entryMap.keySet()) { - WarningEntry entry = entryMap.get(dataUri); - if (entry.shadedShape != null) { - entry.shadedShape.dispose(); - try { - initShape(entry.record); - } catch (VizException e) { - statusHandler.handle(Priority.PROBLEM, - e.getLocalizedMessage(), e); - } - } - } - } - } - issueRefresh(); - } - - protected void paintInternal(IGraphicsTarget target, - PaintProperties paintProps) throws VizException { - FramesInfo info = paintProps.getFramesInfo(); - DataTime[] frames = info.getFrameTimes(); - cleanupData(frames); - int index = info.getFrameIndex(); - if (index > -1 && info.getFrameCount() > 0 - && index < info.getFrameCount()) { - displayedDate = frames[index]; - if (index == info.getFrameCount() - 1) { - // When we are on the last frame, if there exists a frame with - // data that is beyond the last frame but within a reasonable - // amount of time(1 hour) then we will display that frame - // instead of the last frame. We do this so when a new warning - // is issued it will display immediately over radar or satellite - // data, even if the latest frame is a few minutes behind the - // current time. - long displayValid = displayedDate.getMatchValid(); - long latestValid = displayValid; - synchronized (frames) { - for (Entry> entry : this.frames - .entrySet()) { - if (entry.getValue().isEmpty()) { - // do not override the frame time for a time with no - // records. - continue; - } - long valid = entry.getKey().getMatchValid(); - // 3600000 == 1 hour == a magicly random amount of time - if (valid > latestValid - && latestValid - displayValid < 3600000) { - displayedDate = entry.getKey(); - latestValid = valid; - } - } - } - } - } else { - displayedDate = paintProps.getDataTime(); - } - if (displayedDate == null) { - return; - } - - if (!this.recordsToLoad.isEmpty()) { - this.updateFrames(); - } - - DataTime thisFrameTime = displayedDate; - if (!this.frames.containsKey(thisFrameTime)) { - this.initNewFrame(thisFrameTime); - } - - List records = this.frames.get(thisFrameTime); - for (AbstractWarningRecord record : records) { - WarningEntry entry = entryMap.get(record.getDataURI()); - if (entry != null && entry.wireframeShape != null) { - LineStyle lineStyle = (record.getProductClass() != null && record - .getProductClass().equals("T")) ? LineStyle.DASHED - : LineStyle.SOLID; - target.drawWireframeShape(entry.wireframeShape, - getCapability(ColorableCapability.class).getColor(), - getCapability(OutlineCapability.class) - .getOutlineWidth(), lineStyle); - } else if (entry != null && entry.shadedShape != null) { - target.drawShadedShape(entry.shadedShape, 1); - } - - if (record != null && record.getGeometry() != null) { - // Calculate the upper left portion of the polygon - Coordinate upperLeft = new Coordinate(180, -90); - - for (Coordinate c : record.getGeometry().getCoordinates()) { - if (c.y - c.x > upperLeft.y - upperLeft.x) { - upperLeft = c; - } - } - - double[] d = descriptor.worldToPixel(new double[] { - upperLeft.x, upperLeft.y }); - d[0] -= paintProps.getZoomLevel() * 100; - - double mapWidth = descriptor.getMapWidth() - * paintProps.getZoomLevel() / 1000; - String[] textToPrint = getText(record, mapWidth); - if (warningsFont == null) { - warningsFont = target.getDefaultFont().deriveWithSize(11); - } - DrawableString params = new DrawableString(textToPrint, color); - params.font = warningsFont; - params.setCoordinates(d[0], d[1]); - params.textStyle = TextStyle.NORMAL; - params.horizontalAlignment = HorizontalAlignment.RIGHT; - params.verticallAlignment = VerticalAlignment.BOTTOM; - params.magnification = getCapability( - MagnificationCapability.class).getMagnification(); - target.drawStrings(params); - } - } - } - - protected void cleanupData(DataTime[] descFrameTimes) { - List oldFrames = null; - synchronized (frames) { - for (DataTime dt : frames.keySet()) { - boolean found = false; - for (DataTime descTime : descFrameTimes) { - if (descTime.equals(dt)) { - found = true; - break; - } - } - if (!found) { - if (oldFrames == null) { - oldFrames = new ArrayList(); - } - oldFrames.add(dt); - } - } - } - if (oldFrames != null) { - for (DataTime old : oldFrames) { - this.removeFrame(old); - } - } - } - - protected synchronized void initNewFrame(DataTime thisFrameTime) - throws VizException { - // subclasses should override - } - - protected synchronized void updateFrames() throws VizException { - // subclasses should override - } - - protected void initShape(AbstractWarningRecord record) throws VizException { - // subclasses should override - } - - @Override - public String getName() { - return null; - } - - /** - * Schedules a WarningExpirationTask for the end time of the passing in - * record - * - * @param rec - * a WarningRecord - */ - protected void scheduleTimer(AbstractWarningRecord rec) { - // only schedule if record has not expired already - long now = SimulatedTime.getSystemTime().getTime().getTime(); - long endTime = rec.getEndTime().getTimeInMillis(); - synchronized (expTaskSet) { - if (endTime > now && !expTaskSet.contains(new Long(endTime))) { - WarningExpirationTask task = new WarningExpirationTask(this); - timer.schedule(task, rec.getEndTime().getTime()); - expTaskSet.add(new Long(endTime)); - } - } - } - - /** - * Redo time matching and remove the passed in time from the map of - * scheduled times - * - * @param triggerTime - */ - public void redoTimeMatching(long triggerTime) { - redoTimeMatching(); - Long time = new Long(triggerTime); - // remove the instance of the trigger time from the map - synchronized (expTaskSet) { - if (expTaskSet != null && expTaskSet.contains(time)) { - expTaskSet.remove(time); - } - } - } - - /** - * Redo the time matching - */ - public void redoTimeMatching() { - try { - this.getDescriptor().getTimeMatcher().redoTimeMatching(this); - this.getDescriptor().getTimeMatcher() - .redoTimeMatching(this.getDescriptor()); - } catch (VizException e) { - // TODO Auto-generated catch block. Please revise as appropriate. - statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e); - } - } -} diff --git a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/CWASPSResource.java b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/CWASPSResource.java index 1618b7e326..1b457e9cd1 100644 --- a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/CWASPSResource.java +++ b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/CWASPSResource.java @@ -27,6 +27,7 @@ import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKBReader; import com.vividsolutions.jts.io.WKTReader; + public class CWASPSResource extends WarningsResource { private static final transient IUFStatusHandler statusHandler = UFStatus @@ -230,14 +231,14 @@ public class CWASPSResource extends WarningsResource { textToPrint[2] = record.getPil(); - SimpleDateFormat startFormat = AbstractWarningResource.DEFAULT_FORMAT; - SimpleDateFormat endFormat = AbstractWarningResource.DEFAULT_FORMAT; + SimpleDateFormat startFormat = AbstractWWAResource.DEFAULT_FORMAT; + SimpleDateFormat endFormat = AbstractWWAResource.DEFAULT_FORMAT; if (mapWidth == 0) { - startFormat = AbstractWarningResource.LONG_FORMAT; - endFormat = AbstractWarningResource.DAY_FORMAT; + startFormat = AbstractWWAResource.LONG_FORMAT; + endFormat = AbstractWWAResource.DAY_FORMAT; } else if (mapWidth <= 200) { - startFormat = AbstractWarningResource.DAY_FORMAT; - endFormat = AbstractWarningResource.DAY_FORMAT; + startFormat = AbstractWWAResource.DAY_FORMAT; + endFormat = AbstractWWAResource.DAY_FORMAT; } synchronized (startFormat) { diff --git a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WarningRecordComparator.java b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WarningRecordComparator.java index e36b291b9b..2975724182 100644 --- a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WarningRecordComparator.java +++ b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WarningRecordComparator.java @@ -17,7 +17,7 @@ import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * May 12, 2011 jsanchez Initial creation - * + * Sep 27, 2012 1149 jsanchez Updated order of actions. Put CON before CAN,EXP * * * @author jsanchez @@ -30,6 +30,7 @@ public class WarningRecordComparator implements * Compares the WarningRecords by phenSig, ETN, action, then starttime * (descending order) */ + @Override public int compare(AbstractWarningRecord wr1, AbstractWarningRecord wr2) { int rval = 0; @@ -39,19 +40,30 @@ public class WarningRecordComparator implements rval = Double.compare(Double.parseDouble(wr1.getEtn()), Double.parseDouble(wr2.getEtn())); if (rval == 0) { - if (wr1.getAct().equals(wr2.getAct())) { + WarningAction act1 = WarningAction.valueOf(wr1.getAct()); + WarningAction act2 = WarningAction.valueOf(wr2.getAct()); + if (act1 == act2) { rval = 0; - } else if (wr1.getAct().equals(WarningAction.NEW.toString())) { + } else if (act1 == WarningAction.NEW) { rval = -1; - } else if (wr2.getAct().equals(WarningAction.NEW.toString())) { + } else if (act2 == WarningAction.NEW) { rval = 1; + } else if (act1 == WarningAction.CON + && (act2 == WarningAction.CAN || act2 == WarningAction.EXP)) { + return -1; + } else if (act2 == WarningAction.CON + && (act1 == WarningAction.CAN || act1 == WarningAction.EXP)) { + return 1; } else { rval = wr1.getAct().compareTo(wr2.getAct()); } if (rval == 0) { - rval = -1 - * wr1.getStartTime().compareTo(wr2.getStartTime()); + rval = wr1.getStartTime().compareTo(wr2.getStartTime()); + // sort warnings in descending order + if (!wr1.getSig().equals("A")) { + rval *= -1; + } } } } diff --git a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WarningsResource.java b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WarningsResource.java index 586cd0750d..bc02f41e81 100644 --- a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WarningsResource.java +++ b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WarningsResource.java @@ -22,26 +22,27 @@ package com.raytheon.viz.warnings.rsc; import java.util.ArrayList; import java.util.Calendar; -import java.util.Collections; -import java.util.Map; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; import com.raytheon.uf.common.dataplugin.PluginDataObject; import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord; import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction; -import com.raytheon.uf.common.dataquery.requests.RequestConstraint; -import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType; -import com.raytheon.uf.common.status.IUFStatusHandler; -import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.common.time.DataTime; +import com.raytheon.uf.common.time.SimulatedTime; import com.raytheon.uf.viz.core.IGraphicsTarget; -import com.raytheon.uf.viz.core.catalog.LayerProperty; -import com.raytheon.uf.viz.core.datastructure.DataCubeContainer; +import com.raytheon.uf.viz.core.VizApp; import com.raytheon.uf.viz.core.drawables.IDescriptor.FramesInfo; +import com.raytheon.uf.viz.core.drawables.IRenderableDisplay; import com.raytheon.uf.viz.core.drawables.IWireframeShape; import com.raytheon.uf.viz.core.exception.VizException; +import com.raytheon.uf.viz.core.rsc.AbstractVizResource; import com.raytheon.uf.viz.core.rsc.LoadProperties; -import com.raytheon.uf.viz.core.rsc.ResourceType; +import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability; import com.raytheon.viz.core.rsc.jts.JTSCompiler; import com.raytheon.viz.texteditor.util.SiteAbbreviationUtil; import com.vividsolutions.jts.geom.Geometry; @@ -55,31 +56,268 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Sep 1, 2010 jsanchez Initial creation - * Aug 22, 2011 10631 njensen Major refactor + * Aug 22, 2011 10631 njensen Major refactor * May 3, 2012 DR 14741 porricel Stop setting end time of orig. * warning to start time of update. * Jun 04, 2012 DR14992 mgamazaychikov Fix the problem with plotting expiration time for * NEW warning when CAN warning is issued - * + * Sep 27, 2012 1149 jsanchez Refactored methods from AbstractWarningsResource into this class. * * * @author jsanchez * @version 1.0 */ -public class WarningsResource extends AbstractWarningResource { - private static final transient IUFStatusHandler statusHandler = UFStatus - .getHandler(WarningsResource.class); +public class WarningsResource extends AbstractWWAResource { + + protected static class RepaintHeartbeat extends TimerTask { + + private final HashSet> resourceSet = new HashSet>(); + + private boolean cancelled = false; + + public RepaintHeartbeat() { + + } + + /** + * copy resources from old task, just in case some were added after it + * should have been replaced (threads are fun) + **/ + public void copyResourceSet(RepaintHeartbeat oldTask) { + // copy resources, in case one was added after a cancel + Set> oldResourceSet = oldTask + .getResourceSet(); + synchronized (oldResourceSet) { + for (AbstractVizResource rsc : oldResourceSet) { + this.addResource(rsc); + } + } + } + + public Set> getResourceSet() { + return resourceSet; + } + + @Override + public void run() { + // get the unique displays from all the added resources + ArrayList displaysToRefresh = new ArrayList( + 1); + synchronized (resourceSet) { + for (AbstractVizResource rsc : resourceSet) { + try { + IRenderableDisplay disp = rsc.getDescriptor() + .getRenderableDisplay(); + if (!displaysToRefresh.contains(disp)) { + displaysToRefresh.add(disp); + } + } catch (Exception e) { + statusHandler + .handle(Priority.PROBLEM, + "Encountered error during Warnings Heartbeat, continuing with other Warnings ", + e); + } + } + } + + // create an array with final modifier + final IRenderableDisplay[] refreshList = displaysToRefresh + .toArray(new IRenderableDisplay[displaysToRefresh.size()]); + + // execute refersh in UI thread + VizApp.runAsync(new Runnable() { + + @Override + public void run() { + for (IRenderableDisplay disp : refreshList) { + disp.refresh(); + } + } + + }); + + // cancel the task if there are no more resources + boolean cancel = false; + synchronized (resourceSet) { + if (resourceSet.size() < 1) { + cancel = true; + } + } + + if (cancel) { + doCancel(); + } + } + + public void addResource(AbstractVizResource rsc) { + // if task has no resources then it needs to be started when the + // first is added + boolean start = false; + synchronized (resourceSet) { + // if this is the first resource added to an empty set start the + // timer + if (resourceSet.size() < 1) { + start = true; + } + resourceSet.add(rsc); + } + if (start) { + WarningsResource.scheduleHeartBeat(); + } + } + + public void removeResource(AbstractVizResource rsc) { + synchronized (resourceSet) { + resourceSet.remove(rsc); + // cancel the task if there are no more resources + if (resourceSet.size() < 1) { + doCancel(); + } + } + } + + private void doCancel() { + synchronized (heartBeatChangeLock) { + if (cancelled == false) { + cancelled = true; + heartBeatTimer.cancel(); + heartBeatTask = new RepaintHeartbeat(); + heartBeatTimer = new Timer(); + heartBeatTask.copyResourceSet(this); + } + } + } + } + + /** lock when changing heartBeatTask **/ + protected static final Object heartBeatChangeLock = new Object(); + + protected static RepaintHeartbeat heartBeatTask = null; + + protected static Timer heartBeatTimer = null; /** * Constructor */ public WarningsResource(WWAResourceData data, LoadProperties props) { super(data, props); + resourceName = "Warnings"; } @Override - protected synchronized void updateDisplay(IGraphicsTarget target) { + protected void initInternal(IGraphicsTarget target) throws VizException { + DataTime earliest = this.descriptor.getFramesInfo().getFrameTimes()[0]; + requestData(earliest); + synchronized (heartBeatChangeLock) { + if (heartBeatTask == null) { + heartBeatTask = new RepaintHeartbeat(); + } + heartBeatTask.addResource(this); + } + } + + /* + * (non-Javadoc) + * + * @see com.raytheon.viz.core.rsc.IVizResource#dispose() + */ + @Override + protected void disposeInternal() { + synchronized (heartBeatChangeLock) { + heartBeatTask.removeResource(this); + } + for (WarningEntry entry : entryMap.values()) { + if (entry.shadedShape != null) { + entry.shadedShape.dispose(); + } + if (entry.wireframeShape != null) { + entry.wireframeShape.dispose(); + } + } + + entryMap.clear(); + if (warningsFont != null) { + warningsFont.dispose(); + } + } + + @Override + public void resourceChanged(ChangeType type, Object object) { + if (type == ChangeType.DATA_UPDATE) { + PluginDataObject[] pdo = (PluginDataObject[]) object; + synchronized (WarningsResource.this) { + { + try { + addRecord(pdo); + } catch (VizException e) { + statusHandler.handle(Priority.SIGNIFICANT, + e.getLocalizedMessage(), e); + } + } + } + } else if (type == ChangeType.CAPABILITY) { + if (color != null + && color.equals(getCapability((ColorableCapability.class)) + .getColor()) == false) { + color = getCapability((ColorableCapability.class)).getColor(); + + // TODO this needs to be fixed to work with watches which are + // shaded + // for (String dataUri : entryMap.keySet()) { + // WarningEntry entry = entryMap.get(dataUri); + // TODO init a shape somewhere else + // if (entry.shadedShape != null) { + // entry.shadedShape.dispose(); + // try { + // initShape(entry.record); + // } catch (VizException e) { + // statusHandler.handle(Priority.PROBLEM, + // e.getLocalizedMessage(), e); + // } + // } + // } + } + } + issueRefresh(); + } + + @Override + protected void initShape(IGraphicsTarget target, + AbstractWarningRecord record) throws VizException { + Geometry geo; + + if (record.getGeometry() != null) { + try { + WarningEntry entry = entryMap.get(record.getDataURI()); + if (entry == null) { + entry = new WarningEntry(); + entry.record = record; + entryMap.put(record.getDataURI(), entry); + } + IWireframeShape wfs = entry.wireframeShape; + + if (wfs != null) { + wfs.dispose(); + } + + wfs = target.createWireframeShape(false, descriptor); + geo = (Geometry) record.getGeometry().clone(); + + JTSCompiler jtsCompiler = new JTSCompiler(null, wfs, descriptor); + jtsCompiler.handle(geo); + wfs.compile(); + entry.wireframeShape = wfs; + } catch (Exception e) { + statusHandler.handle(Priority.ERROR, + "Error creating wireframe", e); + } + } + } + + @Override + protected synchronized void updateDisplay(IGraphicsTarget target) + throws VizException { if (!this.recordsToLoad.isEmpty()) { FramesInfo info = getDescriptor().getFramesInfo(); DataTime[] frames = info.getFrameTimes(); @@ -121,7 +359,7 @@ public class WarningsResource extends AbstractWarningResource { entry.record.setEndTime((Calendar) warnrec .getStartTime().clone()); } - + if (!rec.getCountyheader().equals( warnrec.getCountyheader()) && act == WarningAction.CAN) { @@ -166,103 +404,50 @@ public class WarningsResource extends AbstractWarningResource { } - @Override - protected void initShape(IGraphicsTarget target, - AbstractWarningRecord record) { - Geometry geo; - - if (record.getGeometry() != null) { - try { - WarningEntry entry = entryMap.get(record.getDataURI()); - if (entry == null) { - entry = new WarningEntry(); - entry.record = record; - entryMap.put(record.getDataURI(), entry); - } - IWireframeShape wfs = entry.wireframeShape; - - if (wfs != null) { - wfs.dispose(); - } - - wfs = target.createWireframeShape(false, descriptor); - geo = (Geometry) record.getGeometry().clone(); - - JTSCompiler jtsCompiler = new JTSCompiler(null, wfs, descriptor); - jtsCompiler.handle(geo); - wfs.compile(); - entry.wireframeShape = wfs; - } catch (Exception e) { - statusHandler.handle(Priority.ERROR, - "Error creating wireframe", e); - } - } - } - - @Override - protected void initInternal(IGraphicsTarget target) throws VizException { - DataTime earliest = this.descriptor.getFramesInfo().getFrameTimes()[0]; - requestData(earliest); + /** + * schedule the heart beat for the next minute + */ + protected static void scheduleHeartBeat() { + // get simulated time + Date currentTime = SimulatedTime.getSystemTime().getTime(); + // get a calendar + Calendar now = Calendar.getInstance(); + // set calendar time to simulated time + now.setTime(currentTime); + // add one to the minutes field + now.add(Calendar.MINUTE, 1); + // reset second and milisecond to 0 + now.set(Calendar.SECOND, 0); + now.set(Calendar.MILLISECOND, 0); + // schedule task to fire every minute synchronized (heartBeatChangeLock) { - if (heartBeatTask == null) { - heartBeatTask = new RepaintHeartbeat(); + try { + if (heartBeatTimer == null) { + heartBeatTimer = new Timer(); + } + // schedule on the minute every minute + heartBeatTimer.schedule(heartBeatTask, now.getTime(), + 1 * 60 * 1000); + } catch (Exception e) { + try { + heartBeatTimer.cancel(); + } catch (Exception e2) { + // ignore, we just want to make sure the timer is cancelled + } finally { + // create a new task if there was an error when scheduling + heartBeatTask = new RepaintHeartbeat(); + heartBeatTimer = new Timer(); + } + statusHandler.handle(Priority.SIGNIFICANT, + "Error scheduling warnings heart beat ", e); } - heartBeatTask.addResource(this); } } - @SuppressWarnings("unchecked") @Override - protected void requestData(DataTime earliest) throws VizException { - System.out.println("requesting data"); - Map map = (Map) resourceData - .getMetadataMap().clone(); - if (earliestRequested != null) { - // don't request data we've already requested - String[] times = new String[] { earliest.toString(), - earliestRequested.toString() }; - RequestConstraint constraint = new RequestConstraint(); - constraint.setConstraintType(ConstraintType.BETWEEN); - constraint.setBetweenValueList(times); - map.put("endTime", constraint); - } else { - RequestConstraint endConstraint = new RequestConstraint( - earliest.toString(), ConstraintType.GREATER_THAN_EQUALS); - map.put("endTime", endConstraint); - } - - earliestRequested = earliest; - - LayerProperty property = new LayerProperty(); - property.setDesiredProduct(ResourceType.PLAN_VIEW); - property.setEntryQueryParameters(map, false); - property.setNumberOfImages(9999); - - Object[] resp = null; - resp = DataCubeContainer.getData(property, 60000).toArray( - new Object[] {}); - PluginDataObject[] arr = new PluginDataObject[resp.length]; - int i = 0; - for (Object o : resp) { - arr[i] = (PluginDataObject) o; - i++; - } - addRecord(sort(arr)); - } - - private PluginDataObject[] sort(PluginDataObject[] pdos) { - ArrayList sortedWarnings = new ArrayList(); - for (Object o : pdos) { - if (((PluginDataObject) o) instanceof AbstractWarningRecord) { - AbstractWarningRecord record = (AbstractWarningRecord) o; - sortedWarnings.add(record); - } - } - - /* Sorts by phensig, etn, starttime (descending), act */ - Collections.sort(sortedWarnings, comparator); - return sortedWarnings.toArray(new AbstractWarningRecord[sortedWarnings - .size()]); + protected String getEventKey(WarningEntry entry) { + AbstractWarningRecord r = entry.record; + return r.getOfficeid() + '.' + r.getPhensig() + '.' + r.getEtn(); } } diff --git a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WatchesResource.java b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WatchesResource.java index ddd7f78d08..558828ed90 100644 --- a/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WatchesResource.java +++ b/cave/com.raytheon.viz.warnings/src/com/raytheon/viz/warnings/rsc/WatchesResource.java @@ -1,12 +1,17 @@ package com.raytheon.viz.warnings.rsc; import java.lang.ref.WeakReference; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; import com.raytheon.uf.common.dataplugin.PluginDataObject; import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord; @@ -17,250 +22,172 @@ import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintTyp import com.raytheon.uf.common.geospatial.ISpatialQuery.SearchMode; import com.raytheon.uf.common.geospatial.SpatialQueryFactory; import com.raytheon.uf.common.geospatial.SpatialQueryResult; -import com.raytheon.uf.common.status.IUFStatusHandler; -import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.common.time.DataTime; -import com.raytheon.uf.common.time.TimeRange; -import com.raytheon.uf.viz.core.catalog.LayerProperty; -import com.raytheon.uf.viz.core.datastructure.DataCubeContainer; +import com.raytheon.uf.common.time.SimulatedTime; +import com.raytheon.uf.viz.core.IGraphicsTarget; import com.raytheon.uf.viz.core.drawables.FillPatterns; import com.raytheon.uf.viz.core.drawables.IShadedShape; import com.raytheon.uf.viz.core.exception.VizException; import com.raytheon.uf.viz.core.rsc.LoadProperties; -import com.raytheon.uf.viz.core.rsc.ResourceType; +import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability; import com.raytheon.viz.core.rsc.jts.JTSCompiler; import com.raytheon.viz.core.rsc.jts.JTSCompiler.PointStyle; -import com.raytheon.viz.texteditor.util.SiteAbbreviationUtil; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.GeometryFactory; -public class WatchesResource extends AbstractWatchesResource { - private static final transient IUFStatusHandler statusHandler = UFStatus - .getHandler(WatchesResource.class); +/** + * + * TODO Add Description + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Sep 27, 2012  1149       jsanchez     Refactored methods from AbstractWarningsResource into this class.
+ * 
+ * 
+ * + * @author jsanchez + * @version 1.0 + */ +public class WatchesResource extends AbstractWWAResource { - private Map> geometryMap = new HashMap>(); + /** + * + * this task calls redoTimeMatching on the resource, it should be scheduled + * to run for when a warning is set to expire + * + * @author ekladstrup + * @version 1.0 + */ + protected class WarningExpirationTask extends TimerTask { + + private WatchesResource rsc = null; + + public WarningExpirationTask(WatchesResource rsc) { + this.rsc = rsc; + } + + @Override + public void run() { + // System.err.println("warning expired"); + // some warning has expired + rsc.redoTimeMatching(this.scheduledExecutionTime()); + rsc.issueRefresh(); + } + + } + + private final Map> geometryMap = new HashMap>(); + + private final Timer timer; + + private final Set expTaskSet; + + protected IGraphicsTarget target; + + private final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm"); public WatchesResource(WWAResourceData data, LoadProperties props) { super(data, props); + timer = new Timer(); + expTaskSet = new HashSet(); + resourceName = "Watches"; } @Override - public String getName() { - DataTime time = displayedDate; - if (time == null) { - time = descriptor.getTimeForResource(this); - } - String name = resourceData.name != null ? resourceData.name : "Watches"; - if (time != null) { - name += " " + time.getLegendString(); - } - return name; - } + protected void initInternal(IGraphicsTarget target) throws VizException { + if (this.target == null) { + this.target = target; - @Override - protected synchronized void initNewFrame(DataTime thisFrameTime) - throws VizException { - long t0 = System.currentTimeMillis(); - HashMap map = (HashMap) resourceData - .getMetadataMap().clone(); - - LayerProperty property = new LayerProperty(); - property.setDesiredProduct(ResourceType.PLAN_VIEW); - property.setNumberOfImages(9999); - - /* - * Retrieve all the watches that have a have a range containing the - * frame time - */ - map.put("startTime", new RequestConstraint(thisFrameTime.toString(), - ConstraintType.LESS_THAN_EQUALS)); - map.put("endTime", new RequestConstraint(thisFrameTime.toString(), - ConstraintType.GREATER_THAN_EQUALS)); - property.setEntryQueryParameters(map, false); - - Object[] resp = DataCubeContainer.getData(property, 60000).toArray( - new Object[] {}); - - ArrayList sortedWatches = new ArrayList(); - for (Object o : resp) { - if (((PluginDataObject) o) instanceof AbstractWarningRecord) { - sortedWatches.add((AbstractWarningRecord) o); - } - } - - /* Sorts by phensig, etn, starttime (descending), act */ - Collections.sort(sortedWatches, comparator); - ArrayList remove = new ArrayList(); - - AbstractWarningRecord conRecord = null; - AbstractWarningRecord expRecord = null; - for (AbstractWarningRecord w : sortedWatches) { - WarningAction act = WarningAction.valueOf(w.getAct()); - // Do not plot watches that have been Cancelled or Expired - if ((act == WarningAction.CAN || act == WarningAction.EXP) - && w.getStartTime().after(thisFrameTime.getValidTime()) == false - && w.getUgcsString() != null - && w.getUgcsString()[0] != null - && w.getUgcsString()[0].endsWith("000")) { - expRecord = w; - } else if (expRecord != null - && expRecord.getEtn().equals(w.getEtn()) - && expRecord.getPhensig().equals(w.getPhensig()) - && expRecord.getStartTime().after(w.getStartTime())) { - - remove.add(w); - } - - // Do not plot watches that out dated CONs - if (act == WarningAction.CON - && (conRecord == null || conRecord.getEtn().equals( - w.getEtn()) == false)) { - conRecord = w; - } else if (conRecord != null - && (act == WarningAction.CON || act == WarningAction.NEW) - && conRecord.getEtn().equals(w.getEtn()) - && conRecord.getPhensig().equals(w.getPhensig()) - && conRecord.getStartTime().after(w.getStartTime())) { - remove.add(w); - } - } - - for (AbstractWarningRecord w : remove) { - sortedWatches.remove(w); - } - - Map watches = new HashMap(); - for (AbstractWarningRecord watchRec : sortedWatches) { - TimeRange tr = new TimeRange(watchRec.getStartTime(), - watchRec.getEndTime()); - if (tr.contains(thisFrameTime.getValidTime().getTime())) { - addWatch(watches, watchRec); - } - } - - ArrayList watchList = new ArrayList(); - if (watches.isEmpty() == false) { - for (String key : watches.keySet()) { - AbstractWarningRecord watch = watches.get(key); - watch.setPil(SiteAbbreviationUtil.getSiteNode(watch.getXxxid()) - + watch.getPil() + watch.getXxxid()); - watchList.add(watch); - scheduleTimer(watch); - initShape(watch); - - WarningEntry entry = entryMap.get(watch.getDataURI()); - if (entry == null) { - entry = new WarningEntry(); - entry.record = watch; - entryMap.put(watch.getDataURI(), entry); + synchronized (this) { + try { + addRecord(getWarningRecordArray()); + } catch (VizException e) { + e.printStackTrace(); } - List list = entry.times; - if (list == null) { - list = new ArrayList(); - } - list.add(thisFrameTime); - entry.times = list; + } + } + // force creation of a frame for any currently active warnings, this + // frame might get displayed in place of the last frame. + requestData(new DataTime(SimulatedTime.getSystemTime().getTime())); + } + + /* + * (non-Javadoc) + * + * @see com.raytheon.viz.core.rsc.IVizResource#dispose() + */ + @Override + protected void disposeInternal() { + timer.cancel(); + + for (WarningEntry entry : entryMap.values()) { + if (entry.shadedShape != null) { + entry.shadedShape.dispose(); + } + if (entry.wireframeShape != null) { + entry.wireframeShape.dispose(); } } - this.frames.put(thisFrameTime, watchList); - System.out.println("Init Frame: " + (System.currentTimeMillis() - t0) - + " ms"); + entryMap.clear(); + if (warningsFont != null) { + warningsFont.dispose(); + } } @Override - protected synchronized void updateFrames() throws VizException { - if (!this.recordsToLoad.isEmpty()) { - for (AbstractWarningRecord watchRec : recordsToLoad) { - WarningAction watchAct = WarningAction.valueOf(watchRec - .getAct()); - TimeRange tr = new TimeRange(watchRec.getStartTime(), - watchRec.getEndTime()); - synchronized (frames) { - for (DataTime dt : frames.keySet()) { - if (tr.contains(dt.getValidTime().getTime())) { - boolean add = true; - ArrayList remove = new ArrayList(); - List watchList = frames - .get(dt); - for (AbstractWarningRecord watch : watchList) { - // Do not plot watches that have been Cancelled - // or - // Expired - if ((watchAct == WarningAction.CAN || watchAct == WarningAction.EXP) - && watchRec.getStartTime().after(dt) == false - && watchRec.getEtn().equals( - watch.getEtn()) - && watchRec.getPhensig().equals( - watch.getPhensig()) - && watchRec.getUgcsString() != null - && watchRec.getUgcsString()[0] != null - && watchRec.getUgcsString()[0] - .endsWith("000")) { - remove.add(watch); - } - if (watchAct == WarningAction.CON - && (WarningAction.valueOf(watch - .getAct()) == WarningAction.CON || WarningAction - .valueOf(watch.getAct()) == WarningAction.NEW) - && watchRec.getEtn().equals( - watch.getEtn()) - && watchRec.getPhensig().equals( - watch.getPhensig())) { - if (watchRec.getStartTime().after( - watch.getStartTime())) { - remove.add(watch); - } else { - add = false; - } - } - } + public void resourceChanged(ChangeType type, Object object) { + if (type == ChangeType.DATA_UPDATE) { + PluginDataObject[] pdo = (PluginDataObject[]) object; + synchronized (WatchesResource.this) { + { + try { + addRecord(pdo); + } catch (VizException e) { + statusHandler.handle(Priority.SIGNIFICANT, + e.getLocalizedMessage(), e); + } + } + } + } else if (type == ChangeType.CAPABILITY) { + if (color != null + && color.equals(getCapability((ColorableCapability.class)) + .getColor()) == false) { + color = getCapability((ColorableCapability.class)).getColor(); - for (AbstractWarningRecord w : remove) { - watchList.remove(w); - } - - if (add) { - watchList.add(watchRec); - scheduleTimer(watchRec); - initShape(watchRec); - - WarningEntry entry = entryMap.get(watchRec - .getDataURI()); - if (entry == null) { - entry = new WarningEntry(); - entry.record = watchRec; - entryMap.put(watchRec.getDataURI(), entry); - } - List list = entry.times; - if (list == null) { - list = new ArrayList(); - } - list.add(dt); - entry.times = list; - } + for (String dataUri : entryMap.keySet()) { + WarningEntry entry = entryMap.get(dataUri); + if (entry.shadedShape != null) { + entry.shadedShape.dispose(); + try { + initShape(target, entry.record); + } catch (VizException e) { + statusHandler.handle(Priority.PROBLEM, + e.getLocalizedMessage(), e); } } } - if (frames.containsKey(new DataTime(watchRec.getStartTime())) == false) { - initNewFrame(new DataTime(watchRec.getStartTime())); - } } - recordsToLoad.clear(); } + issueRefresh(); } @Override - protected void initShape(AbstractWarningRecord record) throws VizException { + protected void initShape(IGraphicsTarget target, + AbstractWarningRecord record) throws VizException { Geometry geo; if (record.getUgczones().size() > 0) { setGeometry(record); if (record.getGeometry() != null) { - IShadedShape ss = target.createShadedShape(false, descriptor, - false); + IShadedShape ss = target.createShadedShape(false, + descriptor.getGridGeometry(), false); geo = (Geometry) record.getGeometry().clone(); JTSCompiler jtsCompiler = new JTSCompiler(ss, null, this.descriptor, PointStyle.CROSS); @@ -279,6 +206,66 @@ public class WatchesResource extends AbstractWatchesResource { } } + @Override + protected synchronized void updateDisplay(IGraphicsTarget target) + throws VizException { + + if (!recordsToLoad.isEmpty()) { + // Merges all the zones for the same vtec and time + List mergedWatches = mergeWatches(recordsToLoad); + for (AbstractWarningRecord watchrec : mergedWatches) { + + WarningAction watchact = WarningAction.valueOf(watchrec + .getAct()); + int watchSize = watchrec.getUgczones().size(); + + if (watchact != WarningAction.NEW) { + AbstractWarningRecord createShape = null; + for (String entryKey : entryMap.keySet()) { + WarningEntry entry = entryMap.get(entryKey); + AbstractWarningRecord rec = entry.record; + + if (rec.getPhensig().equals(watchrec.getPhensig()) + && rec.getOfficeid().equals( + watchrec.getOfficeid()) + && rec.getEtn().equals(watchrec.getEtn())) { + int recSize = rec.getUgczones().size(); + if (!entry.partialCancel) { + if (watchact == WarningAction.EXP + || watchact == WarningAction.CAN) { + entry.partialCancel = true; + entry.record.setEndTime((Calendar) watchrec + .getStartTime().clone()); + } else if (watchact == WarningAction.CON + && recSize > watchSize + && watchrec.getStartTime().after( + rec.getStartTime())) { + entry.partialCancel = true; + entry.record.setEndTime((Calendar) watchrec + .getStartTime().clone()); + createShape = watchrec; + } + } + } + } + + if (createShape != null) { + WarningEntry entry = entryMap.get(createShape + .getDataURI()); + if (entry != null) { + entry.shadedShape.dispose(); + } + initShape(target, createShape); + } + } else { + initShape(target, watchrec); + } + } + + recordsToLoad.clear(); + } + } + private void setGeometry(AbstractWarningRecord record) { List county = new ArrayList(); List marinezone = new ArrayList(); @@ -365,35 +352,90 @@ public class WatchesResource extends AbstractWatchesResource { record.setGeometry(geometry); } - /* - * Combining watches with the same ETN (Watch No.) into a single record. - * This will also help performance to make a single DB query. + /** + * Groups all the ugc zones with the same 'product.act.phensig.etn' */ - private void addWatch(Map watches, - AbstractWarningRecord watchrec) { - String key = watchrec.getProductClass() + "." + watchrec.getPhensig() - + "." + watchrec.getEtn(); - - AbstractWarningRecord watch = null; - WarningAction act = WarningAction.valueOf(watchrec.getAct()); - - if (watches.containsKey(key)) { - watch = watches.get(key); - Set ugcZones = watch.getUgczones(); - for (UGCZone zone : watchrec.getUgczones()) { - if (act == WarningAction.CAN || act == WarningAction.EXP) { - ugcZones.remove(zone); - } else { - ugcZones.add(zone); - } + private List mergeWatches( + List watchrecs) { + Map watches = new HashMap(); + for (AbstractWarningRecord watchrec : watchrecs) { + String key = watchrec.getAct() + '.' + watchrec.getPhensig() + '.' + + watchrec.getEtn() + '.' + + sdf.format(watchrec.getStartTime().getTime()); + AbstractWarningRecord watch = watches.get(key); + if (watch == null) { + watch = watchrec; + } else if (watchrec.getUgczones() != null) { + Set ugcZones = watch.getUgczones(); + ugcZones.addAll(watchrec.getUgczones()); + watch.setUgczones(ugcZones); } - watch.setUgczones(ugcZones); - } else if (act != WarningAction.CAN && act != WarningAction.EXP) { - watch = watchrec; - } - - if (watch != null) { watches.put(key, watch); } + + ArrayList mergedWatches = new ArrayList( + watches.values()); + Collections.sort(mergedWatches, comparator); + + return mergedWatches; } + + /** + * Schedules a WarningExpirationTask for the end time of the passing in + * record + * + * @param rec + * a WarningRecord + */ + protected void scheduleTimer(AbstractWarningRecord rec) { + // only schedule if record has not expired already + long now = SimulatedTime.getSystemTime().getTime().getTime(); + long endTime = rec.getEndTime().getTimeInMillis(); + synchronized (expTaskSet) { + if (endTime > now && !expTaskSet.contains(new Long(endTime))) { + WarningExpirationTask task = new WarningExpirationTask(this); + timer.schedule(task, rec.getEndTime().getTime()); + expTaskSet.add(new Long(endTime)); + } + } + } + + /** + * Redo time matching and remove the passed in time from the map of + * scheduled times + * + * @param triggerTime + */ + public void redoTimeMatching(long triggerTime) { + redoTimeMatching(); + Long time = new Long(triggerTime); + // remove the instance of the trigger time from the map + synchronized (expTaskSet) { + if (expTaskSet != null && expTaskSet.contains(time)) { + expTaskSet.remove(time); + } + } + } + + /** + * Redo the time matching + */ + public void redoTimeMatching() { + try { + this.getDescriptor().getTimeMatcher().redoTimeMatching(this); + this.getDescriptor().getTimeMatcher() + .redoTimeMatching(this.getDescriptor()); + } catch (VizException e) { + // TODO Auto-generated catch block. Please revise as appropriate. + statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e); + } + } + + @Override + protected String getEventKey(WarningEntry entry) { + AbstractWarningRecord r = entry.record; + return r.getAct() + '.' + r.getPhensig() + '.' + r.getEtn() + '.' + + sdf.format(entry.record.getStartTime().getTime()); + } + }