/* ========================
* 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-2004, by :
* Corporate:
* EADS Astrium SAS
* EADS CRC
* Individual:
* Claude Cazenave
*
* $Id: StreamingTSDataSource.java,v 1.21 2008/09/08 10:09:10 ogor Exp $
*
* Changes
* -------
* 11 jan. 2005 : Initial public release (CC);
*
*/
package simtools.data.async;
import java.io.IOException;
import simtools.data.DataException;
import simtools.data.NoSuchIndex;
import simtools.data.UnsupportedOperation;
import simtools.data.async.TimeStampedDataSourceCollection.InvalidFormatException;
/**
* A streaming time stamped data source is a time stamped data source where the
* values are collected dynamically The source is updated using add(time,
* value). It keeps in memory in minimum number of time/values pairs according
* to constructor parameters
*
* @author Claude Cazenave
*/
public class StreamingTSDataSource extends TimeStampedDataSource {
protected int _startIndex;
protected int _lastIndex;
protected double[] _t;
protected double[] _t2;
protected double[] _v;
protected double _dmin;
protected double _dmax;
/**
* The buffer size
*/
protected int _size;
/**
* The buffer minimun size
*/
protected int _minSize;
/**
* Auxiliary dataSource time2
*/
protected TimeSource _time2;
/**
* The minimum duration is used to check when the buffer is filled if we
* have enough samples according to the time elpased between the first and
* the last sample. If it is not the case the new buffer size is twice the
* previous size. Caution, since the duration criterion is met at buffer
* filling, the buffer size is no more modified and the buffer behaves as a
* ring buffer
*/
protected final double _minDuration;
/**
* A maximal size for buffer buffer size
*/
protected int _maxSize = Integer.MAX_VALUE;
/**
* Create a new data source with the following parameters
*
* @param label
* The data source name
* @param id
* The data source id
* @param c
* Its collection parent
* @param minSize
* The minimum number of values to keep
* @param maxSize
* The maximum number of values to keep
* @param minDuration
* The minimun duration of value to keep
* @param withTime2
* If true time2 values are also stored
* @throws IOException
* @throws TimeStampedDataSourceCollection.InvalidFormatException
*/
public StreamingTSDataSource(
String label,
String id,
StreamingTSDataSourceCollection c,
int minSize,
int maxSize,
double minDuration,
boolean withTime2) throws IOException,
TimeStampedDataSourceCollection.InvalidFormatException {
super(label, id, c);
setTime(new StreamingTimeSource("time"));
this._minSize = minSize;
this._maxSize = maxSize;
this._minDuration = minDuration;
initialiseIndex();
_size = _minSize;
_t = new double[_size];
_v = new double[_size];
if (withTime2) {
_time2 = new StreamingTimeSource("time2");
_t2 = new double[_size];
}
_auxiliaries = new AuxiliaryCollection();
}
/**
* Create a new data source with the following parameters
*
* @param label
* The data source name
* @param id
* The data source id
* @param c
* Its collection parent
* @param minSize
* The minimum number of values to keep
* @param maxSize
* The maximum number of values to keep
* @param minDuration
* The minimun duration of value to keep
* @throws IOException
* @throws TimeStampedDataSourceCollection.InvalidFormatException
*/
public StreamingTSDataSource(
String label,
String id,
StreamingTSDataSourceCollection c,
int minSize,
int maxSize,
double minDuration) throws IOException, TimeStampedDataSourceCollection.InvalidFormatException {
this(label, id, c, minSize, maxSize, minDuration, false);
}
/**
* Method initialiseIndex Summary: This method initializes the data source indexes.
* @deprecated : use initialiseIndex() instead
*/
protected void initialiseIndex(int minSize) {
initialiseIndex();
}
/**
* Method initialiseIndex Summary: This method initializes the data source indexes.
*/
public void initialiseIndex() {
_startIndex = -1;
_lastIndex = -1;
}
public void clearBuffers() {
initialiseIndex();
_size = _minSize;
_t = new double[_size];
_v = new double[_size];
if (_time2 != null) {
_t2 = new double[_size];
}
}
/*
* (non-Javadoc)
*
* @see oc.jsynoptic.TimeStampedDataSource#getStart()
*/
public double getStart() throws DataException{
if (_startIndex >= 0) {
return _t[_startIndex % _size];
}
return 0.;
}
/*
* (non-Javadoc)
*
* @see oc.jsynoptic.TimeStampedDataSource#getEnd()
*/
public double getEnd() throws DataException{
if (_lastIndex >= 0) {
return _t[_lastIndex % _size];
}
return 0.;
}
/*
* (non-Javadoc)
*
* @see oc.jsynoptic.TimeStampedDataSource#computeMinMax()
*/
protected void computeMinMax() throws UnsupportedOperation {
// nothing to do : performed on the fly
}
/*
* (non-Javadoc)
*
* @see simtools.data.DataSource#getDoubleMin()
*/
public double getDoubleMin() throws DataException {
return _dmin;
}
/*
* (non-Javadoc)
*
* @see simtools.data.DataSource#getMin()
*/
public Object getMin() {
return new Double(_dmin);
}
/*
* (non-Javadoc)
*
* @see simtools.data.DataSource#getDoubleMin()
*/
public double getDoubleMax() throws DataException {
return _dmax;
}
/*
* (non-Javadoc)
*
* @see simtools.data.DataSource#getMax()
*/
public Object getMax() {
return new Double(_dmax);
}
/*
* (non-Javadoc)
*
* @see simtools.data.ValueProvider#getValue(long)
*/
public Object getValue(long index) throws DataException {
if (_startIndex < 0) {
throw new NoSuchIndex(index);
}
if (index < _startIndex) {
throw new NoSuchIndex(index);
}
if (index > _lastIndex) {
throw new NoSuchIndex(index);
}
return new Double(_v[(int) index % _size]);
}
/*
* (non-Javadoc)
*
* @see simtools.data.ValueProvider#getDoubleValue(long)
*/
public double getDoubleValue(long index) throws DataException {
if (_startIndex < 0) {
throw new NoSuchIndex(index);
}
if (index < _startIndex) {
throw new NoSuchIndex(index);
}
if (index > _lastIndex) {
throw new NoSuchIndex(index);
}
return _v[(int) index % _size];
}
/*
* (non-Javadoc)
*
* @see simtools.data.DataSource#getStartIndex()
*/
public long getStartIndex() throws UnsupportedOperation {
return _startIndex;
}
/*
* (non-Javadoc)
*
* @see simtools.data.DataSource#getLastIndex()
*/
public long getLastIndex() throws UnsupportedOperation {
return _lastIndex;
}
/*
* (non-Javadoc)
*
* @see oc.jsynoptic.TimeStampedDataSource#isValid()
*/
public boolean isValid() {
return _startIndex >= 0;
}
/**
* Add a new pair of time,value and notify listeners
*
* @param t
* @param v
*/
public void add(double t, double v) {
add(t, v, 0);
}
/**
* Add a new time,value,time2 and notify listeners
*
* @param t
* @param v
* @param t2
* @param t2Enabled
*/
public void add(double t, double v, double t2) {
addValues(t, v, t2);
notifyAllEndNotificationListeners();
}
/**
* Add a new time,value,time2
*
* @param t
* @param v
* @param t2
* @param t2Enabled
*/
public void addValues(double t, double v, double t2) {
if (_startIndex < 0) {
_startIndex = 0;
_lastIndex = 0;
_t[0] = t;
_v[0] = v;
if (_t2 != null) {
_t2[0] = t2;
}
_dmin = _dmax = v;
notifyListenersForValueRangeChange();
notifyListenersForIndexRangeChange(0, 0);
} else {
_lastIndex++;
if (_lastIndex >= _size) {
if ((_startIndex == 0) && (_minDuration >= 0.)) {
if (increaseBufferSize()){
int nsize = _size * 2;
double[] nt = new double[nsize];
double[] nv = new double[nsize];
System.arraycopy(_t, 0, nt, 0, _size);
System.arraycopy(_v, 0, nv, 0, _size);
_t = nt;
_v = nv;
if (_t2 != null) {
double[] nt2 = new double[nsize];
System.arraycopy(_t2, 0, nt2, 0, _size);
_t2 = nt2;
}
_size = nsize;
} else {
_startIndex++;
}
} else {
_startIndex++;
}
}
int k = _lastIndex % _size;
_v[k] = v;
_t[k] = t;
if (_t2 != null) {
_t2[k] = t2;
}
// Update sort property
updateSortedOrder();
// Compute min max
if (v < _dmin) {
_dmin = v;
if (v > _dmax) {
_dmax = v;
}
notifyListenersForValueRangeChange();
} else if (v > _dmax) {
_dmax = v;
notifyListenersForValueRangeChange();
}
notifyListenersForIndexRangeChange(_startIndex, _lastIndex);
getTime().notifyListenersForValueRangeChange();
getTime().notifyListenersForIndexRangeChange(_startIndex, _lastIndex);
getTime().notifyEndNotificationListeners();
if (_t2 != null) {
getTime2().notifyListenersForValueRangeChange();
getTime2().notifyListenersForIndexRangeChange(_startIndex, _lastIndex);
}
}
}
/**
*
* @return true if buffer can be increased
*/
protected boolean increaseBufferSize(){
double elapsedTime = _t[_size - 1] - _t[0];
return ( ( elapsedTime < _minDuration) && ((_size * 2) <= _maxSize) );
}
public void notifyAllEndNotificationListeners() {
notifyEndNotificationListeners();
getTime().notifyEndNotificationListeners();
if (_t2 != null) {
getTime2().notifyEndNotificationListeners();
}
}
/**
* @param time
* The time to set.
*/
public void setTime2(TimeSource time2) {
_time2 = time2;
}
/**
* @return Returns the _label.
*/
public TimeSource getTime2() {
return _time2;
}
public class StreamingTimeSource extends TimeSource {
protected String type;
public StreamingTimeSource(String type, String name) throws IOException, InvalidFormatException {
super(name);
this.type = type;
}
public StreamingTimeSource(String type) throws IOException, InvalidFormatException {
this(type, type);
}
/*
* (non-Javadoc)
*
* @see simtools.data.ValueProvider#getValue(long)
*/
public Object getValue(long index) throws DataException {
if (_startIndex < 0) {
throw new NoSuchIndex(index);
}
if (index < _startIndex) {
throw new NoSuchIndex(index);
}
if (index > _lastIndex) {
throw new NoSuchIndex(index);
}
if (type.equals("time")) {
if (_t == null) {
throw new DataException();
}
return new Double(_t[(int) index % _size]);
} else if (type.equals("time2")) {
if (_t2 == null) {
throw new DataException();
}
return new Double(_t2[(int) index % _size]);
} else {
throw new DataException();
}
}
/*
* (non-Javadoc)
*
* @see simtools.data.ValueProvider#getDoubleValue(long)
*/
public double getDoubleValue(long index) throws DataException {
if (_startIndex < 0) {
throw new NoSuchIndex(index);
}
if (index < _startIndex) {
throw new NoSuchIndex(index);
}
if (index > _lastIndex) {
throw new NoSuchIndex(index);
}
if (type.equals("time")) {
return _t[(int) index % _size];
} else if (type.equals("time2")) {
return _t2[(int) index % _size];
} else {
throw new DataException();
}
}
public Object getMin() throws UnsupportedOperation {
try {
return new Double(StreamingTSDataSource.this.getStart());
} catch (DataException e) {
throw new UnsupportedOperation(e.getMessage());
}
}
public Object getMax() throws UnsupportedOperation {
try {
return new Double(StreamingTSDataSource.this.getEnd());
} catch (DataException e) {
throw new UnsupportedOperation(e.getMessage());
}
}
}
protected class AuxiliaryCollection extends TimeStampedDataSource.AuxiliaryCollection {
/*
* (non-Javadoc)
*
* @see java.util.AbstractList#get(int)
*/
public Object get(int index) {
Object ret = super.get(index);
if (ret != null) {
return ret;
}
if (index == 1) {
if (_time2 != null) {
return _time2;
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see java.util.AbstractCollection#size()
*/
public int size() {
return super.size() + (_time2 == null ? 0 : 1);
}
}
}