package onlinefrontlines.profiler;
import java.util.TimerTask;
/**
* Class that contains changes of a single value over time
*
* @author jorrit
*
* Copyright (C) 2009-2013 Jorrit Rouwe
*
* This file is part of Online Frontlines.
*
* Online Frontlines is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Online Frontlines 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Online Frontlines. If not, see <http://www.gnu.org/licenses/>.
*/
public final class TimeSeries extends TimerTask
{
/**
* Rate at which we're sampling
*/
public final static long MS_PER_SAMPLE = 120 * 1000;
/**
* How long we want to keep each sample
*/
public final static long TIME_TO_KEEP_SAMPLE = 24L * 60 * 60 * 1000;
/**
* How many samples we must keep
*/
public final static int MAX_SAMPLES = (int)(TIME_TO_KEEP_SAMPLE / MS_PER_SAMPLE);
/**
* Accumulation types
*/
public static enum Type
{
RATE,
AVERAGE,
VALUE,
MIN,
MAX
}
/**
* Type for this series
*/
private Type type;
/**
* Callback
*/
private TimeSeriesCallback callback;
/**
* Start time of current sample
*/
private long startTime;
/**
* Current accumulated value
*/
private double accumulatedValue;
/**
* Amount of times accumulate was called
*/
private int accumulateCount;
/**
* List of values over time
*/
private double[] values = new double[MAX_SAMPLES];
/**
* How many values have been collected
*/
private int valueCount;
/**
* Which value to overwrite next
*/
private int nextValue;
/**
* Constructor
*/
public TimeSeries(Type type, TimeSeriesCallback callback)
{
this.type = type;
this.callback = callback;
resetAccumulated();
}
/**
* Reset time series
*/
public synchronized void reset()
{
valueCount = 0;
nextValue = 0;
resetAccumulated();
}
/**
* Accumulate value
*
* @param value
*/
public synchronized void accumulate(double value)
{
if (accumulateCount == 0)
{
// First value is always stored
accumulatedValue = value;
}
else
{
// Next values are accumulated
switch (type)
{
case MIN:
accumulatedValue = Math.min(accumulatedValue, value);
break;
case MAX:
accumulatedValue = Math.max(accumulatedValue, value);
break;
default:
accumulatedValue += value;
break;
}
}
++accumulateCount;
}
/**
* Reset accumulation system
*/
private void resetAccumulated()
{
accumulatedValue = 0;
accumulateCount = 0;
startTime = System.nanoTime();
}
/**
* Called by the timer
*/
public synchronized void run()
{
if (callback != null)
accumulate(callback.getValue());
double next = 0;
switch (type)
{
case RATE:
long deltaTime = System.nanoTime() - startTime;
next = 1.0e9 * accumulatedValue / deltaTime;
break;
case AVERAGE:
next = accumulateCount > 0? accumulatedValue / accumulateCount : 0;
break;
case MIN:
case MAX:
case VALUE:
next = accumulatedValue;
break;
default:
assert(false);
break;
}
values[nextValue] = next;
nextValue = (nextValue + 1) % MAX_SAMPLES;
valueCount = Math.min(valueCount + 1, MAX_SAMPLES);
resetAccumulated();
}
/**
* Get all values
*/
public synchronized double[] getValues()
{
double[] outValues = new double[valueCount];
for (int out = valueCount - 1, in = nextValue; out >= 0; --out)
{
// Determine input value
in--;
if (in < 0)
in = MAX_SAMPLES - 1;
// Write output value
outValues[out] = values[in];
}
return outValues;
}
}