/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Nicolas Brodu
*
* $Id: RandomDataSourceCollection.java,v 1.4 2008/02/25 11:33:36 ogor Exp $
*
* Changes
* -------
* 27-Nov-2003 : Creation Date (NB);
*
*/
package examples.random;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
import javax.swing.undo.CompoundEdit;
import jsynoptic.base.ContextualActionProvider;
import simtools.data.DataInfo;
import simtools.data.DataSource;
import simtools.data.DynamicDataSourceCollection;
import simtools.data.UnsupportedOperation;
import simtools.data.ValueProvider;
import simtools.data.buffer.DelayedBuffer;
/**
* This example class can serves as the basis to create your own dynamic data sources.
* It creates data sources of random data between 0 and 100 on demand.
*
* The following optional interfaces are implemented as an example.
* - ContextualActionProvider : adds a contextual popup menu when right-clicking on the collection in the source pane
*
*/
public class RandomDataSourceCollection extends DynamicDataSourceCollection implements ActionListener, ContextualActionProvider {
/** We use a timer to produce values periodically
*/
protected Timer timer;
/**
* This constructor sets up the timer and one time source.
* The time source is used to create XY plots, so we can see
* the evolution of random variables over time.
*/
public RandomDataSourceCollection() {
// Create a first source called "time", useful to make XY plots.
// See addSource() for comments
createDataSource(new DataInfo("time","time","the time","s"), ValueProvider.DoubleProvider);
// Wrap a buffer around it so we see past values too
try {
bufferize(0, new DelayedBuffer(ValueProvider.DoubleProvider, 100));
} catch (UnsupportedOperation e) {
e.printStackTrace();
}
// Calls ourselves each half of a second
timer = new Timer(500, this);
}
/** Starts producing data automatically
*/
public void start() {
timer.start();
}
/** Stops producing data automatically
*/
public void stop() {
timer.stop();
}
/** Produce exactly one data in each source
*/
public void step() {
// The API of the dynamic data source is as follow:
// - Set the values for each source using setTypeValue
// - Register the values when they are ready with registerNewValues
// The last index used is stored in parent class => use it to compute the index for our data sources
long curIndex = lastIndex + 1;
// First source is the time
// Simply use the index, as we're called each half second
setDoubleValue(0, curIndex / 2.0);
// For all other sources, add random data
for (int i=1; i<size(); ++i) {
// On init, start at 50
if (curIndex==0) setDoubleValue(i,50);
// Then, add or remove a random quantity, be remain bounded between 0 and 100
else {
double value = sourceInfo[i].cachedDouble;
value += Math.random()*10 -5 ;
if (value>100) value = 100;
if (value<0) value = 0;
setDoubleValue(i,value);
}
}
// Now register the values to the system.
registerNewValues();
}
/** Adds a new data source to the collection
*/
public void addSource() {
// Specify the data information
DataInfo di = new DataInfo(
"Random source "+size(), // Label of the data source : what is displayed
"random"+size(), // Id of the data source : must be unique. Also used by the source providers (see the random plugin)
"random values", // A comment, optional
"unit" // this data source unit, optional
);
// Create the source, with data type double
createDataSource(di, ValueProvider.DoubleProvider);
// Wrap a buffer around it so we see past values too
try {
bufferize(size() -1, new DelayedBuffer(ValueProvider.DoubleProvider, 100));
} catch (UnsupportedOperation e) {
e.printStackTrace();
}
}
// -----------------------------------------------------------------------
// Data Source Collection methods
// -----------------------------------------------------------------------
/**
* If a source is sorted, this can lead to some optimization.
*/
public int sortedOrder(int i) {
if (i==0)
return 1; // time is in ascending order
return super.sortedOrder(i);
}
/**
* Returns information about the collection itself
*/
public DataInfo getInformation() {
return new DataInfo(
"Random Collection", // The name of this data source collection, as appears in the source pane
"RandomCollection" // A unique ID. This example supposes there is only one instance of this object
);
}
// -----------------------------------------------------------------------
// Interface used by the Swing Timer .
// -----------------------------------------------------------------------
/**
* This method is called by the timer each time it triggers (that is, every half second for us)
*/
public void actionPerformed(ActionEvent e) {
// Automatically call step() to produce new data.
// It would be wise to synchronize the step method too... but let's keep this simple.
step();
}
// -----------------------------------------------------------------------
// ContextualActionProvide interface.
// This adds a contextual popup menu when
// right-clicking on the collection in the source pane
// -----------------------------------------------------------------------
/**
* Return the list of possible actions
* The contect information may be used, or not.
*
* In the case of a popup menu in the source pane, the context is SOURCELIST_CONTEXT
* @param x Coordinate, mouse position in the same unit as contains(x,y)
* @param y Coordinate, mouse position in the same unit as contains(x,y)
* @param o Object the actions should work on. Possibly null => default or all actions
* @param context one of the context defined in the ContextualActionProvider class
* @return The list of possible actions, possibly null or an empty array
*/
public String[] getActions(double x, double y, Object o, int context) {
if (context!=SOURCELIST_CONTEXT) return null;
// If the timer is running, we can stop it.
// It would be possible to add a source while running, with synchronize.
// See DynamicDataSourceCollection.createDataSource(...) for details.
// Now, let's keep this example simple, shall we?
if (timer.isRunning()) return new String[] { "Stop" };
// Otherwise, we can either start the timer or add values step by step
// - start, to start adding values automatically,
// - step, to add only one value
// Removing or adding sources is possible too, but don't remove our time source
if (size()>1) return new String[] { "Start", "Step", "Add Source", "Remove Source" };
else return new String[] { "Start", "Step", "Add Source" };
}
/**
* Do one of the actions previously declared by getAction.
*
* @param x Coordinate, for example mouse position
* @param y Coordinate, for example mouse position
* @param o Object the action should work on.
* @param action An action returned by a previous getActions call with the same x, y, o parameters
* It may be null, in which case the default action is requested for this x,y,o.
* @return true if the action could be performed
*/
public boolean doAction(double x, double y, Object o, String action, CompoundEdit undoableEdit) {
if (action.equals("Start")) {
start();
}
if (action.equals("Step")) {
step();
}
if (action.equals("Stop")) {
stop();
}
if (action.equals("Add Source")) {
addSource();
}
if (action.equals("Remove Source")) {
this.removeDataSource((DataSource)lastElement()); // uses vector API
}
return false;
}
/**
* Returns true if, and only if, it is possible to do the action right now
* @param x Coordinate, for example mouse position
* @param y Coordinate, for example mouse position
* @param o Object the action should work on.
* @param action An action returned by a previous getActions call with the same x, y, o parameters
* It may be null, in which case the default action is requested for this x,y,o.
* @param context one of the context defined in the ContextualActionProvider class
* @return true if the action can be performed
*/
public boolean canDoAction(double x, double y, Object o, String action, int context) {
// This example object is always ready to perform the actions it declared
return true;
}
}