/* * This file is part of the OpenSCADA project * Copyright (C) 2006-2011 TH4 SYSTEMS GmbH (http://th4-systems.com) * * OpenSCADA is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenSCADA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenSCADA. If not, see * <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License. */ package org.openscada.da.ui.client.chart.view; import java.util.Calendar; import java.util.Collection; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IMemento; import org.eclipse.ui.IViewSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.ViewPart; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYStepRenderer; import org.jfree.chart.title.LegendTitle; import org.jfree.data.time.FixedMillisecond; import org.jfree.data.time.RegularTimePeriod; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.time.TimeSeriesDataItem; import org.jfree.experimental.chart.swt.ChartComposite; import org.openscada.core.NotConvertableException; import org.openscada.core.NullValueException; import org.openscada.core.Variant; import org.openscada.da.client.DataItemValue; import org.openscada.da.ui.client.chart.Activator; import org.openscada.da.ui.client.chart.Messages; import org.openscada.da.ui.connection.data.DataItemHolder; import org.openscada.da.ui.connection.data.DataSourceListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ChartView2 extends ViewPart { private final static Logger logger = LoggerFactory.getLogger ( ChartView2.class ); public final static String VIEW_ID = "org.openscada.da.ui.client.chart.ChartView"; //$NON-NLS-1$ private static final int REFRESH_DELAY = 1000; private ChartComposite frame = null; private JFreeChart chart = null; private final TimeSeriesCollection dataset; private Display display; private static class Item implements DataSourceListener { private final org.openscada.da.ui.connection.data.Item item; private final TimeSeries series; private final ChartView2 chartView; private DataItemHolder dataItem; private DataItemValue value; public Item ( final org.openscada.da.ui.connection.data.Item item, final TimeSeriesCollection dataset, final ChartView2 chartView ) { this.item = item; this.series = new TimeSeries ( getLabel (), FixedMillisecond.class ); dataset.addSeries ( this.series ); this.chartView = chartView; } public org.openscada.da.ui.connection.data.Item getItem () { return this.item; } public String getLabel () { return this.item.getId (); } public void connect () { this.dataItem = new DataItemHolder ( Activator.getDefault ().getBundle ().getBundleContext (), this.item, this ); } public void disconnect () { if ( this.dataItem != null ) { this.dataItem.dispose (); this.dataItem = null; } } public void performUpdate ( final DataItemValue value ) { this.value = value; final Number n = convertToNumber ( value ); final RegularTimePeriod time = new FixedMillisecond ( Calendar.getInstance ().getTime () ); final TimeSeriesDataItem di = new TimeSeriesDataItem ( time, n ); // final long end = this.series.getMaximumItemAge (); // final long now = time.getLastMillisecond (); this.series.add ( di ); // this.chart.getXYPlot ().addDomainMarker ( new IntervalMarker ( end, now ) ); } @Override public void updateData ( final DataItemValue value ) { this.chartView.update ( this, value ); } } private final Collection<Item> items = new CopyOnWriteArrayList<Item> (); public ChartView2 () { this.dataset = new TimeSeriesCollection (); } @Override public void createPartControl ( final Composite parent ) { try { this.display = parent.getDisplay (); this.chart = createChart (); this.frame = new ChartComposite ( parent, SWT.NONE, this.chart, true ); this.frame.pack (); scheduleUpdate (); } catch ( final Throwable e ) { logger.debug ( "Failed", e ); //$NON-NLS-1$ } } private void scheduleUpdate () { if ( !this.display.isDisposed () ) { this.display.timerExec ( REFRESH_DELAY, new Runnable () { @Override public void run () { triggerUpdate (); scheduleUpdate (); } } ); } } private JFreeChart createChart () { final ValueAxis timeAxis = new DateAxis ( Messages.getString ( "ChartView.axis.time" ) ); //$NON-NLS-1$ timeAxis.setLowerMargin ( 0.02 ); // reduce the default margins timeAxis.setUpperMargin ( 0.02 ); final NumberAxis valueAxis = new NumberAxis ( Messages.getString ( "ChartView.axis.value" ) ); //$NON-NLS-1$ valueAxis.setAutoRangeIncludesZero ( false ); // override default final XYPlot plot = new XYPlot ( this.dataset, timeAxis, valueAxis, null ); XYToolTipGenerator toolTipGenerator = null; toolTipGenerator = StandardXYToolTipGenerator.getTimeSeriesInstance (); // final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer ( true, false ); final XYStepRenderer renderer = new XYStepRenderer (); renderer.setBaseToolTipGenerator ( toolTipGenerator ); plot.setRenderer ( renderer ); final JFreeChart chart = new JFreeChart ( Messages.getString ( "ChartView.chartTitle" ), JFreeChart.DEFAULT_TITLE_FONT, plot, false ); //$NON-NLS-1$ chart.addLegend ( new LegendTitle ( plot ) ); return chart; } @Override public void setFocus () { if ( this.frame != null ) { this.frame.setFocus (); } } @Override public void dispose () { disconnect (); if ( this.frame != null ) { this.frame.dispose (); this.frame = null; } super.dispose (); } protected void disconnect () { for ( final Item item : this.items ) { item.disconnect (); } this.items.clear (); } public void addItem ( final org.openscada.da.ui.connection.data.Item item ) { Item charItem; this.items.add ( charItem = new Item ( item, this.dataset, this ) ); charItem.connect (); } protected void triggerUpdate () { if ( !this.display.isDisposed () ) { final ChartComposite frame = this.frame; if ( frame != null ) { frame.getDisplay ().asyncExec ( new Runnable () { @Override public void run () { if ( !ChartView2.this.frame.isDisposed () ) { performUpdate (); } } } ); } } } protected void triggerUpdate ( final Item item, final DataItemValue value ) { if ( !this.display.isDisposed () ) { final ChartComposite frame = this.frame; if ( frame != null ) { frame.getDisplay ().asyncExec ( new Runnable () { @Override public void run () { if ( !ChartView2.this.frame.isDisposed () ) { item.performUpdate ( value ); // ChartView2.this.frame.forceRedraw (); } } } ); } } } protected static Number convertToNumber ( final DataItemValue div ) { if ( div == null ) { return null; } final Variant value = div.getValue (); Number n = null; try { n = value.asDouble (); } catch ( final NullValueException e ) { } catch ( final NotConvertableException e ) { } if ( n == null ) { try { n = value.asLong (); } catch ( final NullValueException e ) { } catch ( final NotConvertableException e ) { } } return n; } @Override public void init ( final IViewSite site, final IMemento memento ) throws PartInitException { if ( memento != null ) { final IMemento[] childs = memento.getChildren ( "item" ); if ( childs != null ) { for ( final IMemento child : childs ) { final org.openscada.da.ui.connection.data.Item item = org.openscada.da.ui.connection.data.Item.loadFrom ( child ); if ( item != null ) { addItem ( item ); } } } } super.init ( site, memento ); } @Override public void saveState ( final IMemento memento ) { for ( final Item item : this.items ) { final IMemento child = memento.createChild ( "item" ); item.getItem ().saveTo ( child ); } super.saveState ( memento ); } protected void performUpdate () { for ( final Item item : this.items ) { item.performUpdate ( item.value ); } // update this.frame.forceRedraw (); } public void update ( final Item item, final DataItemValue value ) { triggerUpdate ( item, value ); } }