Print

Print


Author: [log in to unmask]
Date: Mon Mar 16 13:03:30 2015
New Revision: 2465

Log:
Add support for multiple series in strip charts and other updates.  Not in its final form yet.

Added:
    java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/ValueProvider.java
Modified:
    java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/MonitoringPlotFactory.java
    java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartBuilder.java
    java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartUpdater.java
    java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatistics.java
    java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatisticsImpl.java
    java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalStripChartTestDriver.java
    java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/et/EtSystemStripCharts.java

Modified: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/MonitoringPlotFactory.java
 =============================================================================
--- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/MonitoringPlotFactory.java	(original)
+++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/MonitoringPlotFactory.java	Mon Mar 16 13:03:30 2015
@@ -13,6 +13,7 @@
 
 import org.jfree.chart.ChartPanel;
 import org.jfree.chart.JFreeChart;
+import org.jfree.data.time.RegularTimePeriod;
 
 /**
  * This class implements an AIDA <code>IPlotterFactory</code> for the monitoring application. It
@@ -23,6 +24,7 @@
  */
 public class MonitoringPlotFactory extends PlotterFactory {
     
+    // Global plotter registry.
     static PlotterRegistry plotters = new PlotterRegistry();
     
     // The name of the factory which will be used in naming tabs in the monitoring app.
@@ -34,16 +36,22 @@
     // Root pane where this factory's top-level tab will be inserted.
     private static JTabbedPane rootPane = null;
 
+    // The region listener for handling mouse clicks in a region.
     private static PlotterRegionListener regionListener;
     
+    // The current tab index.
     int tabIndex;
 
+    /**
+     * Set the plot region listener.
+     * @param regionListener The plot region listener.
+     */
     public static void setPlotterRegionListener(PlotterRegionListener regionListener) {
         MonitoringPlotFactory.regionListener = regionListener;
     }
 
     /**
-     * Class constructor.
+     * Class constructor for unnamed factory.
      */
     MonitoringPlotFactory() {
         super();
@@ -54,7 +62,7 @@
     }
 
     /**
-     * Class constructor.
+     * Class constructor for named factory.
      * @param name The name of the factory.
      */
     MonitoringPlotFactory(String name) {
@@ -66,6 +74,10 @@
             addPlotterRegionListener(regionListener);
     }
 
+    /**
+     * Setup the root GUI pane of this factory for display of plots in tabs.
+     * @param name The tab's label.
+     */
     private void setupRootPane(String name) {
         // FIXME: Hack to disregard call from an AIDA related class.
         if (!(new RuntimeException()).getStackTrace()[2].getClassName().equals("hep.aida.ref.plotter.style.registry.StyleStoreXMLReader")) {
@@ -102,6 +114,11 @@
         MonitoringPlotFactory.rootPane = rootPane;
     }
 
+    /**
+     * Setup a plotter tab.
+     * @param plotterName The name of the plotter.
+     * @param plotter The IPlotter which will plot into the tab.
+     */
     private void setupPlotterTab(String plotterName, IPlotter plotter) {
         
         // Setup the plotter's GUI pane and tab.
@@ -115,29 +132,72 @@
         plotters.register(plotter, tabIndex, plotterIndex);
     }
 
+    /**
+     * Add a <code>JFreeChart</code> to one of the tabs.
+     * @param chart The JFreeChart object to add.
+     */
     private void addChart(JFreeChart chart) {
         ChartPanel panel = new ChartPanel(chart);
         tabs.addTab(chart.getTitle().getText(), panel);
         tabs.setTabComponentAt(tabs.getTabCount() - 1, new JLabel(chart.getTitle().getText()));
     }
-
-    /**
-     * Create a strip chart using a JFreeChart implementation. It will be automatically updated from
-     * a {@link StripChartUpdater}. Similar to AIDA plots, the chart will be given a sub-tab in the
-     * tab of this factory.
-     * 
-     * @param title The title of the chart.
-     * @param yAxisLabel The y axis label.
-     * @param size The buffer size of the series which determines how much data displays.
-     * @param updater The updater which will update the chart in real time.
-     * @return The modified <tt>StripChartUpdater</tt> which points to the new chart.
-     */
-    public StripChartUpdater createStripChart(String title, String yAxisLabel, int size, StripChartUpdater updater) {
-        JFreeChart stripChart = StripChartBuilder.createDynamicTimeSeriesChart(title, yAxisLabel, size);
-        stripChart.getLegend().setVisible(false); /* Legend turned off for now. */
-        addChart(stripChart);
-        updater.setChart(stripChart);
+    
+    /**
+     * This creates a strip chart with full parameter settings, which will automatically
+     * update at a certain time interval.
+     * @param name The title of the chart.
+     * @param rangeLabel The range axis label text.
+     * @param seriesCount The number of series in the data set.
+     * @param seriesNames The names of the series (if non-null the length must match seriesCount).
+     * @param itemCount The maximum number of items in the series.
+     * @param timeBase The time unit for updates.
+     * @param valueProvider The interface for providing the series values.
+     * @param rangeView The view in the domain axis around the current data point (applied to plus and minus).
+     * @return The StripChartUpdater for the chart.
+     */
+    public StripChartUpdater createStripChart(
+            String name, 
+            String rangeLabel,
+            int seriesCount, 
+            String[] seriesNames,
+            int itemCount,
+            RegularTimePeriod timeBase,
+            ValueProvider valueProvider,
+            long rangeView) {
+        StripChartUpdater updater = StripChartBuilder.createStripChart(
+                name, 
+                rangeLabel, 
+                seriesCount, 
+                seriesNames,
+                itemCount, 
+                timeBase, 
+                valueProvider, 
+                rangeView);
+        addChart(updater.getChart());
         return updater;
+    }
+    
+    /**
+     * Create a strip chart with simple parameter settings.
+     * @param name The title of the strip chart.
+     * @param seriesCount The number of series in the data set.
+     * @param timeBase The time interval for updating.
+     * @param valueProvider The interface for providing values.
+     * @return The StripChartUpdater for the chart.
+     */
+    public StripChartUpdater createStripChart(
+            String name, 
+            int seriesCount, 
+            RegularTimePeriod timeBase,
+            ValueProvider valueProvider) {
+        return createStripChart(
+                name, "Values", 
+                seriesCount, 
+                null, 
+                9999, 
+                timeBase, 
+                valueProvider, 
+                10000L);
     }
 
     /**
@@ -157,8 +217,8 @@
     }       
     
     /**
-     * 
-     * @return
+     * Get the global registry of plotters.
+     * @return The global plotter registry.
      */
     public static PlotterRegistry getPlotterRegistry() {
         return plotters;

Modified: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartBuilder.java
 =============================================================================
--- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartBuilder.java	(original)
+++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartBuilder.java	Mon Mar 16 13:03:30 2015
@@ -1,45 +1,91 @@
 package org.hps.monitoring.plotting;
-
-import java.util.Date;
 
 import org.jfree.chart.ChartFactory;
 import org.jfree.chart.JFreeChart;
 import org.jfree.chart.axis.NumberAxis;
 import org.jfree.chart.plot.XYPlot;
 import org.jfree.data.time.DynamicTimeSeriesCollection;
+import org.jfree.data.time.RegularTimePeriod;
 import org.jfree.data.time.Second;
 import org.jfree.data.time.TimeSeries;
 import org.jfree.data.time.TimeSeriesCollection;
 
 /**
- * Utility methods for building strip charts using JFreeChart backend.
+ * Utility methods for building strip charts using JFreeChart back end.
  */
 public final class StripChartBuilder {
 
     private StripChartBuilder() {
     }
+    
+    /**
+     * Create a strip chart with simple parameter settings.
+     * @param name The title of the strip chart.
+     * @param seriesCount The number of series in the data set.
+     * @param timeBase The time interval for updating.
+     * @param valueProvider The interface for providing values.
+     * @return The StripChartUpdater for the chart.
+     */
+    static StripChartUpdater createStripChart(
+            String name, 
+            int seriesCount, 
+            RegularTimePeriod timeBase,
+            ValueProvider valueProvider) {
+        return createStripChart(name, "Values", seriesCount, null, 9999, timeBase, valueProvider, 10000L);
+    }
+    
+    /**
+     * This creates a strip chart with full parameter settings, which will automatically
+     * update at a certain time interval.
+     * @param name The title of the chart.
+     * @param rangeLabel The range axis label text.
+     * @param seriesCount The number of series in the data set.
+     * @param seriesNames The names of the series (if non-null the length must match seriesCount).
+     * @param itemCount The maximum number of items in the series.
+     * @param timeBase The time unit for updates.
+     * @param valueProvider The interface for providing the series values.
+     * @param rangeView The view in the domain axis around the current data point (milliseconds).
+     * @return The StripChartUpdater for the chart.
+     */
+    static StripChartUpdater createStripChart(
+            String name, 
+            String rangeLabel,
+            int seriesCount, 
+            String[] seriesNames,
+            int itemCount,
+            RegularTimePeriod timeBase,
+            ValueProvider valueProvider,
+            long rangeView) {
+                
+        if (seriesNames != null && seriesCount != seriesNames.length) {
+            throw new IllegalArgumentException("seriesNames is wrong length");
+        }
+        final DynamicTimeSeriesCollection dataset = new DynamicTimeSeriesCollection(seriesCount, itemCount, timeBase);
+        dataset.setTimeBase(timeBase);
+        for (int series = 0; series < seriesCount; series++) {
+            String seriesName = name + " " + series;
+            if (seriesNames != null) {
+                seriesName = seriesNames[series];
+            }
+            dataset.addSeries(new float[] {}, series, seriesName);
+        }
+        
+        final JFreeChart chart = ChartFactory.createTimeSeriesChart(name, "hh:mm:ss", rangeLabel, dataset, false, false, false);
+        
+        chart.getXYPlot().getRangeAxis().setAutoRange(true);
 
-    /**
-     * This creates a strip chart that will be updated at fixed intervals from a timer.
-     * @param title
-     * @param yAxisLabel
-     * @param size
-     * @return
-     */
-    public static JFreeChart createDynamicTimeSeriesChart(String title, String yAxisLabel, int size) {
-        final DynamicTimeSeriesCollection dataset = new DynamicTimeSeriesCollection(1, size, new Second());
-        dataset.setTimeBase(new Second(new Date()));
-        dataset.addSeries(new float[] {}, 0, "Default Dataset");
-
-        final JFreeChart result = ChartFactory.createTimeSeriesChart(title, "hh:mm:ss", yAxisLabel, dataset, true, true, false);
-        final XYPlot plot = result.getXYPlot();
-        plot.getDomainAxis().setAutoRange(true);
-        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
-        rangeAxis.setAutoRange(true);
-        rangeAxis.setAutoRangeIncludesZero(true);
-        return result;
+        StripChartUpdater updater = new StripChartUpdater(
+                chart, 
+                valueProvider,
+                rangeView,
+                timeBase
+                );
+        
+        updater.start();
+        
+        return updater;
     }
-
+        
     /**
      * This should be used when the time period for updating is variable.
      * 
@@ -47,13 +93,18 @@
      * 
      * <code>sensorSeries.add(new Minute(new Date()), newData);</code>
      * 
-     * @param title
-     * @param yAxisLabel
-     * @param maxAge
-     * @param maxCount
-     * @return
+     * @param title The title of the chart.
+     * @param yAxisLabel The range axis label.
+     * @param maxAge The maximum age of an item.
+     * @param maxCount The maximum count of items in the single data set series.
+     * @return The chart that was created.
      */
-    public static JFreeChart createTimeSeriesChart(String title, String yAxisLabel, int maxAge, int maxCount, int rangeSize) {
+    public static JFreeChart createTimeSeriesChart(
+            String title, 
+            String yAxisLabel, 
+            int maxAge, 
+            int maxCount,
+            int rangeSize) {
 
         TimeSeriesCollection dataset = new TimeSeriesCollection();
         TimeSeries timeSeries = new TimeSeries("Default Dataset");
@@ -61,7 +112,14 @@
         timeSeries.setMaximumItemCount(maxCount);
         dataset.addSeries(timeSeries);
 
-        final JFreeChart result = ChartFactory.createTimeSeriesChart(title, "hh:mm:ss", yAxisLabel, dataset, true, true, false);
+        final JFreeChart result = ChartFactory.createTimeSeriesChart(
+                title, 
+                "hh:mm:ss", 
+                yAxisLabel, 
+                dataset, 
+                true, 
+                false, 
+                false);
         final XYPlot plot = result.getXYPlot();
         plot.getDomainAxis().setAutoRange(true);
         plot.getDomainAxis().setAutoRangeMinimumSize(rangeSize);
@@ -69,6 +127,63 @@
         rangeAxis.setAutoRange(true);
         rangeAxis.setAutoRangeIncludesZero(true);
         return result;
-    }
+    }    
+    
+    /**
+     * <p>
+     * This can be used to create a strip chart with multiple <code>TimeSeries</code> 
+     * in the data set.
+     * <p>
+     * To update a chart of this type, use the following types of method calls:
+     * <pre>
+     * dataset.getSeries(0).add(new Second(time), value1);
+     * dataset.getSeries(1).add(new Second(time), value2);
+     * </pre>
+     * <p>
+     * It is not updated manually but will refresh will values are added to the backing dataset.
+     * 
+     * @param title The title of the chart.
+     * @param yAxisLabel The range axis label.
+     * @param seriesCount The number of series in the dataset.
+     * @param datasetNames The names of the datasets (can be null to use defaults).
+     * @param rangeSize The range of values to show for auto-ranging in domain axis (in milliseconds).
+     * @return The chart that was created.
+     */
+    public static JFreeChart createTimeSeriesChart(
+            String title, 
+            String yAxisLabel, 
+            int seriesCount,
+            String[] datasetNames,
+            double rangeSize) {
 
+        TimeSeriesCollection dataset = new TimeSeriesCollection();
+        for (int i = 0; i < seriesCount; i++) {
+            String datasetName = "Dataset " + i;
+            if (datasetNames != null) {
+                datasetName = datasetNames[i];
+            }
+            TimeSeries timeSeries = new TimeSeries(datasetName);
+            dataset.addSeries(timeSeries);
+        }
+               
+        final JFreeChart result = ChartFactory.createTimeSeriesChart(
+                title, 
+                "hh:mm:ss", 
+                yAxisLabel, 
+                dataset, 
+                true, 
+                false, 
+                false);
+        final XYPlot plot = result.getXYPlot();
+        plot.getDomainAxis().setAutoRange(true);
+
+        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
+        rangeAxis.setAutoRange(true);
+        rangeAxis.setAutoRangeIncludesZero(true);
+        
+        plot.getDomainAxis().setAutoRange(true);
+        plot.getDomainAxis().setAutoRangeMinimumSize(rangeSize);
+        
+        return result;
+    }        
 }

Modified: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartUpdater.java
 =============================================================================
--- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartUpdater.java	(original)
+++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/StripChartUpdater.java	Mon Mar 16 13:03:30 2015
@@ -1,38 +1,67 @@
 package org.hps.monitoring.plotting;
 
+import java.util.Date;
 import java.util.Timer;
 import java.util.TimerTask;
 
 import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.DateAxis;
 import org.jfree.data.time.DynamicTimeSeriesCollection;
+import org.jfree.data.time.RegularTimePeriod;
 
 /**
  * An abstract <tt>TimerTask</tt> to update a strip chart at a regular interval.
  */
-public abstract class StripChartUpdater extends TimerTask {
+public class StripChartUpdater {
+    
+    final JFreeChart chart;
+    final DateAxis domainAxis;
+    final DynamicTimeSeriesCollection dataset;
+    final Timer timer;
+    final TimerTask task;
+    final Long rangeMillis;
+    final ValueProvider valueProvider;
+    long updateInterval;
+        
+    StripChartUpdater(JFreeChart chart, ValueProvider valueProvider, Long rangeMillis, RegularTimePeriod timeBase) {
+        this.chart = chart;                        
+        this.domainAxis = (DateAxis) chart.getXYPlot().getDomainAxis();
+        this.dataset = (DynamicTimeSeriesCollection) chart.getXYPlot().getDataset();
+        this.rangeMillis = rangeMillis;
+        this.updateInterval = timeBase.getLastMillisecond() - timeBase.getFirstMillisecond();            
+        this.valueProvider = valueProvider;
+        timer = new Timer(chart.getTitle().getText() + " Timer");
+        task = new TimerTask() {
+    
+            @Override
+            public void run() {
+                StripChartUpdater.this.chart.setNotify(false);
+        
+                dataset.advanceTime();
+                long time = dataset.getNewestTime().getEnd().getTime();
+                
+                float values[] = StripChartUpdater.this.valueProvider.getValues();
+                dataset.appendData(values);
+        
+                domainAxis.setRange(
+                        new Date(time - StripChartUpdater.this.rangeMillis), 
+                        new Date(time + updateInterval));
 
-    DynamicTimeSeriesCollection dataset;
-    long updateIntervalMillis = 1000;
-
-    public StripChartUpdater() {
+                StripChartUpdater.this.chart.setNotify(true);
+                StripChartUpdater.this.chart.fireChartChanged();                  
+            }            
+        };
     }
-
-    public void setChart(JFreeChart chart) {
-        this.dataset = (DynamicTimeSeriesCollection) chart.getXYPlot().getDataset();
+    
+    JFreeChart getChart() {
+        return chart;
     }
-
-    public void setUpdateIntervalMillis(long updateIntervalMillis) {
-        this.updateIntervalMillis = updateIntervalMillis;
+     
+    void start() {
+        timer.scheduleAtFixedRate(task, 0, updateInterval);
+    }        
+    
+    public void stop() {
+        timer.cancel();
     }
-
-    public void run() {
-        dataset.advanceTime();
-        dataset.appendData(new float[] { nextValue() });
-    }
-
-    public void schedule(Timer timer) {
-        timer.schedule(this, 0, updateIntervalMillis);
-    }
-
-    public abstract float nextValue();
 }

Added: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/ValueProvider.java
 =============================================================================
--- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/ValueProvider.java	(added)
+++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/plotting/ValueProvider.java	Mon Mar 16 13:03:30 2015
@@ -0,0 +1,14 @@
+package org.hps.monitoring.plotting;
+
+/**
+ * Simple interface for providing values to charts (e.g. strip charts) by series. 
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public interface ValueProvider {        
+    
+    /**
+     * Get an array of float values to fill in the data set series.
+     * @return The array of data set values, ordered by ascending series number.
+     */
+    float[] getValues();
+}

Modified: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatistics.java
 =============================================================================
--- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatistics.java	(original)
+++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatistics.java	Mon Mar 16 13:03:30 2015
@@ -1,7 +1,6 @@
 package org.hps.monitoring.subsys;
 
 import java.io.PrintStream;
-import java.util.TimerTask;
 
 /**
  * This is an interface for a set of basic statistics about an online event processing system.
@@ -129,5 +128,5 @@
      * Add subtask which will execute right before a new tick.
      * @param subtask The subtask to execute.
      */
-    void addSubTask(TimerTask subtask);
+    //void addSubTask(TimerTask subtask);
 }

Modified: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatisticsImpl.java
 =============================================================================
--- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatisticsImpl.java	(original)
+++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/SystemStatisticsImpl.java	Mon Mar 16 13:03:30 2015
@@ -2,22 +2,18 @@
 
 import java.io.PrintStream;
 import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Timer;
 import java.util.TimerTask;
 
-import org.hps.monitoring.plotting.StripChartUpdater;
+import org.hps.monitoring.plotting.ValueProvider;
 
 /**
  * Implementation of {@link SystemStatistics}.
  */
-// FIXME: Rolling averages need to happen over a greater time period like 30 seconds
-// instead of 1 second, because otherwise the statistics don't look right.
 public class SystemStatisticsImpl implements SystemStatistics {
 
-    long tickLengthMillis = 1000; // default is one second tick
-    long sessionElapsedMillis;
+    long tickLengthMillis = 1000; // default is 1 second tick
+    long totalElapsedMillis;
     long startTimeMillis;
     long stopTimeMillis;
     long eventsSinceTick;
@@ -25,14 +21,12 @@
     long totalEvents;
     long totalBytes;
     long tickStartMillis;
-    long tickElapsedMillis;
     static final long Kb = 1 * 1024;
     static final long Mb = Kb * 1024;
     static final double milliToSecond = 0.001;
     static final DecimalFormat decimalFormat = new DecimalFormat("#.####");
     Timer timer;
-    List<TimerTask> subtasks = new ArrayList<TimerTask>();
-
+    
     @Override
     public void update(int size) {
         addEvent();
@@ -52,7 +46,7 @@
 
     @Override
     public long getTotalElapsedMillis() {
-        return sessionElapsedMillis;
+        return totalElapsedMillis;
     }
 
     @Override
@@ -67,7 +61,7 @@
     
     @Override
     public long getTickElapsedMillis() {
-        return tickElapsedMillis;
+        return System.currentTimeMillis() - tickStartMillis;
     }
 
     /**
@@ -86,10 +80,11 @@
     
     @Override
     public double getEventsPerSecond() {
-        if (eventsSinceTick > 0 && tickElapsedMillis > 0)
-            return (double) eventsSinceTick / millisToSeconds(tickElapsedMillis);
-        else
+        if (eventsSinceTick > 0 && getTickElapsedMillis() > 0) {
+            return (double) eventsSinceTick / millisToSeconds(getTickElapsedMillis());
+        } else {
             return 0.;
+        }
     }
 
     @Override
@@ -126,8 +121,8 @@
    
     @Override
     public double getBytesPerSecond() {
-        if (bytesSinceTick > 0 && tickElapsedMillis > 0)
-            return (double) bytesSinceTick / millisToSeconds(tickElapsedMillis);
+        if (bytesSinceTick > 0 && getTickElapsedMillis() > 0)
+            return (double) bytesSinceTick / millisToSeconds(getTickElapsedMillis());
         else
             return 0.;
     }
@@ -135,20 +130,14 @@
     @Override
     public void start() {
 
-        // Set time variables.
+        // Set session start time variables.
         long currentTimeMillis = System.currentTimeMillis();
         startTimeMillis = currentTimeMillis;
         tickStartMillis = currentTimeMillis;
 
-        // Start Timer task which executes at tick length.
+        // Start timer task which executes at the nominal tick length to calculate statistics periodically.
         TimerTask task = new TimerTask() {
             public void run() {
-
-                // Run sub-tasks.
-                for (TimerTask subtask : subtasks) {
-                    subtask.run();
-                }
-
                 nextTick();
             }
         };
@@ -185,12 +174,7 @@
         ps.println("  eventsSinceTick = " + this.getEventsReceived());
         ps.println("  bytesSinceTick = " + this.getBytesReceived());
     }
-
-    @Override
-    public void addSubTask(TimerTask subtask) {
-        this.subtasks.add(subtask);
-    }
-
+   
     void addEvent() {
         eventsSinceTick += 1;
         totalEvents += 1;
@@ -202,8 +186,7 @@
     }
 
     void updateElapsedTime() {
-        tickElapsedMillis = System.currentTimeMillis() - tickStartMillis;
-        sessionElapsedMillis = System.currentTimeMillis() - startTimeMillis;
+        totalElapsedMillis = System.currentTimeMillis() - startTimeMillis;
     }
 
     // Bytes to megabytes to 2 decimal places.
@@ -218,82 +201,76 @@
     synchronized void nextTick() {
         eventsSinceTick = 0;
         bytesSinceTick = 0;
-        tickElapsedMillis = 0;
         tickStartMillis = System.currentTimeMillis();
     }
     
-    public abstract class SystemStatisticsUpdater extends StripChartUpdater {
-        SystemStatisticsUpdater() {
-            addSubTask(this);
-        }
-    }
-    
-    public class AverageEventsPerSecondUpdater extends SystemStatisticsUpdater {
-
-        @Override
-        public float nextValue() {
-            return (float) getAverageEventsPerSecond();
-        }
-    }
-    
-    public class EventsPerSecondUpdater extends SystemStatisticsUpdater {
-
-        @Override
-        public float nextValue() {
-            return (float) getEventsPerSecond();
+    public abstract class SystemStatisticsProvider implements ValueProvider {
+    }
+    
+    public class AverageEventsPerSecondProvider extends SystemStatisticsProvider {
+
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getAverageEventsPerSecond()};
+        }
+    }
+    
+    public class EventsPerSecondProvider extends SystemStatisticsProvider {
+
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getEventsPerSecond()};
         }
     }
             
-    public class EventsReceivedUpdater extends SystemStatisticsUpdater {
-
-        @Override
-        public float nextValue() {
-            return getEventsReceived();
-        }
-    }
-
-    public class TotalEventsUpdater extends SystemStatisticsUpdater {
-        @Override
-        public float nextValue() {
-            return getTotalEvents();
-        }
-    }
-
-    public class BytesReceivedUpdater extends SystemStatisticsUpdater {
-        @Override
-        public float nextValue() {
-            return getBytesReceived();
-        }
-    }
-
-    public class AverageMegabytesPerSecondUpdater extends SystemStatisticsUpdater {
-        @Override
-        public float nextValue() {
-            return (float) getAverageMegabytesPerSecond();
-        }
-    }
-
-    public class TotalMegabytesUpdater extends SystemStatisticsUpdater {
-        @Override
-        public float nextValue() {
-            return (float) getTotalMegabytes();
-        }
-    }
-    
-    public class BytesPerSecondUpdater extends SystemStatisticsUpdater {
-
-        @Override
-        public float nextValue() {
-            return (float) getBytesPerSecond();
-        }
-    }
-    
-    public class MegabytesPerSecondUpdater extends SystemStatisticsUpdater {
-        @Override
-        public float nextValue() {
-            return (float) getBytesPerSecond() / 1000000;
-        }
-    }
-    
-    
-}
+    public class EventsReceivedProvider extends SystemStatisticsProvider {
+
+        @Override
+        public float[] getValues() {
+            return new float[] {getEventsReceived()};
+        }
+    }
+
+    public class TotalEventsProvider extends SystemStatisticsProvider {
+        @Override
+        public float[] getValues() {
+            return new float[] {getTotalEvents()};
+        }
+    }
+
+    public class BytesReceivedProvider extends SystemStatisticsProvider {
+        @Override
+        public float[] getValues() {
+            return new float[]{getBytesReceived()};
+        }
+    }
+
+    public class AverageMegabytesPerSecondProvider extends SystemStatisticsProvider {
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getAverageMegabytesPerSecond()};
+        }
+    }
+
+    public class TotalMegabytesProvider extends SystemStatisticsProvider {
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getTotalMegabytes()};
+        }
+    }
+    
+    public class BytesPerSecondProvider extends SystemStatisticsProvider {
+
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getBytesPerSecond()};
+        }
+    }
+    
+    public class MegabytesPerSecondProvider extends SystemStatisticsProvider {
+        @Override
+        public float[] getValues() {
+            return new float[] {(float) getBytesPerSecond() / 1000000};
+        }
+    }
+}

Modified: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalStripChartTestDriver.java
 =============================================================================
--- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalStripChartTestDriver.java	(original)
+++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/ecal/EcalStripChartTestDriver.java	Mon Mar 16 13:03:30 2015
@@ -1,13 +1,9 @@
 package org.hps.monitoring.subsys.ecal;
 
-import java.util.Date;
-import java.util.TimerTask;
-
 import org.hps.monitoring.plotting.MonitoringPlotFactory;
-import org.hps.monitoring.plotting.StripChartUtil;
-import org.jfree.chart.JFreeChart;
-import org.jfree.data.time.Millisecond;
-import org.jfree.data.time.TimeSeries;
+import org.hps.monitoring.plotting.StripChartUpdater;
+import org.hps.monitoring.plotting.ValueProvider;
+import org.jfree.data.time.Second;
 import org.lcsim.event.EventHeader;
 import org.lcsim.event.RawCalorimeterHit;
 import org.lcsim.util.Driver;
@@ -21,22 +17,27 @@
     int eventInterval = 1000;
     static String collectionName = "EcalReadoutHits";
 
-    MonitoringPlotFactory plotFactory = (MonitoringPlotFactory) AIDA.defaultInstance().analysisFactory().createPlotterFactory("ECAL System Monitoring");
-    TimeSeries series;
-    JFreeChart stripChart;
-    TimerTask updateTask;
+    MonitoringPlotFactory plotFactory = 
+            (MonitoringPlotFactory) AIDA.defaultInstance().analysisFactory().createPlotterFactory("ECAL System Monitoring");
+
     EventHeader currentEvent;
     int hits;
+    
     int events;
-
+    double averageHits;
+    
+    StripChartUpdater updater;
+       
     public void startOfData() {
-        stripChart = plotFactory.createStripChart("Average ECAL Hits per " + eventInterval + " Events", "Hits", 99999999, /*
-                                                                                                                           * max
-                                                                                                                           * age
-                                                                                                                           */
-                1000, /* max count */
-                100000 /* range size */);
-        series = StripChartUtil.getTimeSeries(stripChart);
+        plotFactory.createStripChart(
+                "Average ECAL Hits per " + eventInterval + " Events", 
+                "Hits", 
+                1, 
+                new String[] { "Date" }, 
+                1, 
+                new Second(), 
+                new AverageHitsProvider(), 
+                20000L);        
     }
 
     public void process(EventHeader event) {
@@ -44,10 +45,19 @@
         ++events;
         hits += size;
         if (event.getEventNumber() % eventInterval == 0) {
-            double averageHits = (double) hits / (double) events;
-            series.add(new Millisecond(new Date()), averageHits);
+            averageHits = (double) hits / (double) events;
             hits = 0;
             events = 0;
         }
     }
+    
+    public void endOfData() {
+        updater.stop();
+    }
+    
+    class AverageHitsProvider implements ValueProvider {
+        public float[] getValues() {
+            return new float[] {(float) averageHits};
+        }
+    }
 }

Modified: java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/et/EtSystemStripCharts.java
 =============================================================================
--- java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/et/EtSystemStripCharts.java	(original)
+++ java/trunk/monitoring-util/src/main/java/org/hps/monitoring/subsys/et/EtSystemStripCharts.java	Mon Mar 16 13:03:30 2015
@@ -1,8 +1,13 @@
 package org.hps.monitoring.subsys.et;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.hps.monitoring.plotting.MonitoringPlotFactory;
+import org.hps.monitoring.plotting.StripChartUpdater;
 import org.hps.monitoring.subsys.SystemStatisticsImpl;
 import org.hps.record.et.EtEventProcessor;
+import org.jfree.data.time.Second;
 import org.jlab.coda.et.EtEvent;
 import org.lcsim.util.aida.AIDA;
 
@@ -13,21 +18,68 @@
 
     SystemStatisticsImpl stats = new SystemStatisticsImpl();
     MonitoringPlotFactory plotFactory = (MonitoringPlotFactory) AIDA.defaultInstance().analysisFactory().createPlotterFactory("ET System Monitoring");
-
+    List<StripChartUpdater> updaters = new ArrayList<StripChartUpdater>();
+    
     public EtSystemStripCharts() {
-        stats.setTickLengthMillis(2000);
+        stats.setTickLengthMillis(1000);
     }
     
     /**
      * Setup the strip charts for ET system monitoring and start accumulating statistics.
      */
     @Override
-    public void startJob() {        
-        plotFactory.createStripChart("Data Rate", "MB / second", 100, stats.new MegabytesPerSecondUpdater()); 
-        plotFactory.createStripChart("Total Data", "Megabytes", 100, stats.new TotalMegabytesUpdater());      
-        plotFactory.createStripChart("Event Rate", "Events / second",  100, stats.new EventsPerSecondUpdater());
-        plotFactory.createStripChart("Total Events", "Number of Events", 100, stats.new TotalEventsUpdater());
+    public void startJob() {
+
+        // Create the ET system strip charts.
+        createStripCharts();
+
+        // Start systems statistics task.
         stats.start();
+    }
+
+    /**
+     * 
+     */
+    private void createStripCharts() {
+        updaters.add(plotFactory.createStripChart(
+                "Data Rate", 
+                "MB / second", 
+                1, 
+                new String[] { "Data" }, 
+                999, 
+                new Second(), 
+                stats.new MegabytesPerSecondProvider(), 
+                200000L));
+
+        updaters.add(plotFactory.createStripChart(
+                "Total Data", 
+                "Megabytes", 
+                1, 
+                new String[] { "Data" },
+                999,
+                new Second(), 
+                stats.new TotalMegabytesProvider(), 
+                200000L));
+        
+        updaters.add(plotFactory.createStripChart(
+                "Event Rate", 
+                "Events / s", 
+                1, 
+                new String[] { "Data" }, 
+                999, 
+                new Second(), 
+                stats.new EventsPerSecondProvider(), 
+                200000L));
+        
+        updaters.add(plotFactory.createStripChart(
+                "Total Events", 
+                "Number of Events", 
+                1, 
+                new String[] { "Data" }, 
+                999, 
+                new Second(), 
+                stats.new TotalEventsProvider(), 
+                200000L));
     }
 
     @Override
@@ -35,8 +87,14 @@
         stats.update(event.getLength());
     }
 
-    @Override
     public void endJob() {
+
+        // Stop the strip chart updaters.
+        for (StripChartUpdater updater : updaters) {
+            updater.stop();
+        }
+        
+        // Stop system statistics task.
         stats.stop();
     }
 }