package com.plectix.simulator.streaming;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.plectix.simulator.simulator.ThreadLocalData;
import com.plectix.simulator.util.io.PlxLogger;
public final class LiveDataStreamer {
private static final LiveDataConsumerInterface DUMMY_LIVE_DATA_CONSUMER = new LiveDataConsumerInterface() {
@Override
public void addDataPoint(long currentEventNumber, double currentTime) {
}
@Override
public void consumeLiveData() {
}
@Override
public LiveData getConsumedLiveData() {
return null;
}
};
private static final PlxLogger LOGGER = ThreadLocalData.getLogger(LiveDataStreamer.class);
private static final ScheduledExecutorService TIMER = new ScheduledThreadPoolExecutor(1);
private int liveDataInterval = -1;
private LiveDataConsumerInterface consumer;
private ScheduledFuture<?> consumeTaskFuture;
private final Runnable consumeTask = new Runnable() {
@Override
public void run() {
consumer.consumeLiveData();
}
};
/**
* Default Constructor
*/
public LiveDataStreamer() {
super();
consumer = DUMMY_LIVE_DATA_CONSUMER;
}
/**
* Adds a new data point to be processed for live data.
*
* Warning: This method should return very fast since it is called from the main simulation thread.
*
* @param currentEventNumber
* @param currentTime
* @param observables
*/
public final void addNewDataPoint(long currentEventNumber, double currentTime) {
if (liveDataInterval <= 0) {
// the live data feature is turned off so don't add the new data point
return;
}
consumer.addDataPoint(currentEventNumber, currentTime);
}
/**
* Returns live data to the client.
*
* This method is called from a thread that is separate from the main simulation thread
* so that the client doesn't need to worry about returning immediately from this method.
* This means that liveData that is passed to this method should be an independent copy
* of the data that is not modified while this method is running.
*
* @param liveData
* @return
*/
public final LiveData getLiveData() {
// return live data asynchronously, i.e. the returned data may have been prepared by the periodic consumption some time earlier...
return consumer.getConsumedLiveData();
}
/**
* Resets the data streaming queues and structures.
*
* This method is called from the main simulation thread just before a new simulation starts.
*
* @param liveDataInterval
* @param liveDataPoints
* @param observables
*/
public final void reset(final int liveDataInterval, final int liveDataPoints, final String liveDataConsumerClassname, final LiveDataSourceInterface liveDataSource) {
if (liveDataInterval <= 0) {
// the live data feature is turned off so let' return
LOGGER.debug("Live Data Streamer is turned off.");
return;
}
this.liveDataInterval = liveDataInterval;
stop();
try {
Class[] parameterTypes = new Class[] { LiveDataSourceInterface.class, int.class };
Object[] initargs = new Object[] {liveDataSource, liveDataPoints};
this.consumer = (LiveDataConsumerInterface) Class.forName(liveDataConsumerClassname).getConstructor(parameterTypes).newInstance(initargs);
} catch (Exception exception) {
LOGGER.debug("Could not instantiate " + liveDataConsumerClassname + " -> Live Data Streamer is turned off!");
this.consumer = DUMMY_LIVE_DATA_CONSUMER;
exception.printStackTrace();
return;
}
consumeTaskFuture = TIMER.scheduleAtFixedRate(consumeTask, liveDataInterval, liveDataInterval, TimeUnit.MILLISECONDS);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Live Data Streamer is turned on with interval = " + liveDataInterval + " milliseconds and points = " + liveDataPoints);
}
}
public final void stop() {
if (consumeTaskFuture != null) {
consumeTaskFuture.cancel(false);
TIMER.execute(consumeTask);
consumer = DUMMY_LIVE_DATA_CONSUMER;
}
}
}