/******************************************************************************* * Mission Control Technologies, Copyright (c) 2009-2012, United States Government * as represented by the Administrator of the National Aeronautics and Space * Administration. All rights reserved. * * The MCT platform is licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * MCT includes source code licensed under additional open source licenses. See * the MCT Open Source Licenses file included with this distribution or the About * MCT Licenses dialog available at runtime from the MCT Help menu for additional * information. *******************************************************************************/ package gov.nasa.arc.mct.fastplot.view; import gov.nasa.arc.mct.components.FeedProvider; import gov.nasa.arc.mct.components.FeedProvider.FeedType; import gov.nasa.arc.mct.components.FeedProvider.RenderingInfo; import java.awt.Color; import java.util.Collection; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PlotDataFeedUpdateHandler { private final static Logger logger = LoggerFactory .getLogger(PlotDataFeedUpdateHandler.class); private PlotViewManifestation plotViewManifestation; private static final RenderingInfo DEFAULT_RI = new RenderingInfo(null, Color.WHITE, "0", Color.WHITE, false); PlotDataFeedUpdateHandler( PlotViewManifestation supportedPlotViewManifestation) { plotViewManifestation = supportedPlotViewManifestation; } void synchronizeTime(Map<String, List<Map<String, String>>> data, long syncTime) { GregorianCalendar calendar = new GregorianCalendar(); calendar.setTimeInMillis(syncTime); plotViewManifestation.getPlot().showTimeSyncLine(calendar); updateFromFeeds(data, true, true, false); } void updateFromFeed(Map<String, List<Map<String, String>>> data, boolean predictionOnly) { plotViewManifestation.getPlot().informUpdateFromFeedEventStarted(); // Receiving any data from the feed informs us that any sync lines // should be // removed. // Check if we cached updates while the updateFromFeed was previously // locked. updateFromFeeds(data, false, true, predictionOnly); plotViewManifestation.getPlot().refreshDisplay(); // Request that the plot updates its display with the new feed data. if (plotViewManifestation.controlPanel != null && plotViewManifestation.controlPanel.isShowing()) { //plotViewManifestation.controlPanel.refreshDisplay(); //TODO: Reconnect so that time marches on } plotViewManifestation.getPlot().informUpdateFromFeedEventCompleted(); } public void processData(Map<String, List<Map<String, String>>> data) { if (logger.isDebugEnabled()) { logger.debug("\n Recived new slice {}", printDataOnSlice(data)); } boolean currentCompressionState = plotViewManifestation.getPlot().isCompressionEnabled(); try { plotViewManifestation.getPlot().setCompressionEnabled(false); updateFromFeeds(data, false, false, false); } finally { plotViewManifestation.getPlot().setCompressionEnabled(currentCompressionState); } } public void startDataRequest() { plotViewManifestation.getPlot().informUpdateDataEventStarted(); plotViewManifestation.getPlot().refreshDisplay(); } public void endDataRequest() { closeOutProcessData(); } private void closeOutProcessData() { if (plotViewManifestation.controlPanel != null && plotViewManifestation.controlPanel.isShowing()) { //plotViewManifestation.controlPanel.refreshDisplay(); //TODO: Time must march on } // unlock the plot's update events. plotViewManifestation.getPlot().informUpdateDataEventCompleted(); plotViewManifestation.getPlot().refreshDisplay(); } /** * Unwrap the data from the feed service. * * @param feedIds * the set of feed IDs * @param data * the data */ void updateFromFeeds(Map<String, List<Map<String, String>>> data, boolean legendOnly, boolean updateLegend, boolean predictionOnly) { if (data != null) { Map<String, SortedMap<Long, Double>> dataForPlot = new HashMap<String, SortedMap<Long,Double>>(); // Iterate over the feeds (each is a plot line on the chart) Collection<FeedProvider> feeds = plotViewManifestation .getVisibleFeedProviders(); for (FeedProvider provider : feeds) { if (provider.getFeedType() != FeedType.STRING) { String feedId = provider.getSubscriptionId(); List<Map<String, String>> dataForThisFeed = data.get(feedId); // This prevents "live" data (from the current time, not the maximum valid time) from predictive feeds from // showing up in the plot. This prevents a bug where live data arrives in the middle of a historical data request, and // the plot thinks the next historical slice is redundant and ignores it (since it overlaps with the live data). boolean allowPlotting = predictionOnly == provider.isPrediction() || !updateLegend; if (dataForThisFeed != null && plotViewManifestation.getPlot().isKnownDataSet(feedId) && allowPlotting) { SortedMap<Long, Double> dataForPlotThisFeed = dataForPlot.get(feedId); if(dataForPlotThisFeed == null) { dataForPlotThisFeed = new TreeMap<Long, Double>(); dataForPlot.put(feedId, dataForPlotThisFeed); } RenderingInfo lastRI = DEFAULT_RI; boolean haveLegendInfo = false; // Loop over each point that needs to be plotted for this // feed. for (Map<String, String> pointsData : dataForThisFeed) { RenderingInfo ri = provider .getRenderingInfo(pointsData); assert pointsData != null : "PointsData is Null"; String timeAsString = pointsData .get(FeedProvider.NORMALIZED_TIME_KEY); String valueAsString = ri.getValueText(); boolean isPlottable = ri.isPlottable(); // Robust to time or value keys not being present. if (timeAsString != null && valueAsString != null) { try { long milliSecondsEpoch = Long .parseLong(timeAsString); if (!isPlottable) { valueAsString = ""; } lastRI = ri; haveLegendInfo = true; if (!plotViewManifestation.getPlot() .isKnownDataSet(feedId)) { plotViewManifestation.getPlot().addDataSet( feedId, provider.getLegendText()); } if (!legendOnly) { double value; if(isPlottable) { value = Double.parseDouble(valueAsString); } else { value = Double.NaN; } dataForPlotThisFeed.put(milliSecondsEpoch, value); } } catch (NumberFormatException e) { logger .error( "Number format exception converting string to double while processing the data feed entry {}, {}", timeAsString, valueAsString); } } else { logger .error( "Either time, value, or isValid entry was not defined. {}, {}", timeAsString, valueAsString); } } if (haveLegendInfo && updateLegend) { plotViewManifestation.getPlot().updateLegend(feedId, lastRI); } } } } plotViewManifestation.getPlot().addData(dataForPlot); } else { logger.debug("Data was null"); } } private String printDataOnSlice(Map<String, List<Map<String, String>>> data) { long earliestTime = Long.MAX_VALUE; long latestTime = -Long.MAX_VALUE; int numberDataPoints = 0; for (String feedId : data.keySet()) { List<Map<String, String>> dataForThisFeed = data.get(feedId); if (dataForThisFeed != null) { for (Map<String, String> pointsData : dataForThisFeed) { assert pointsData != null : "PointsData is Null"; String timeAsString = pointsData .get(FeedProvider.NORMALIZED_TIME_KEY); if (timeAsString != null) { try { numberDataPoints++; long milliSecondsEpoch = Long .parseLong(timeAsString); if (milliSecondsEpoch > latestTime) { latestTime = milliSecondsEpoch; } if (milliSecondsEpoch < earliestTime) { earliestTime = milliSecondsEpoch; } assert latestTime >= earliestTime : "latest < earliest!"; } catch (NumberFormatException e) { logger .error( "Number format exception converting string to double while processing the data feed entry {}", timeAsString); } } else { logger .error( "Either time, value, or isValid entry was not defined. {}", timeAsString); } } } } if (numberDataPoints > 0) { return "Slice: " + numberDataPoints; } else { return "Slice: " + numberDataPoints + " [ .. ]"; } } }