/*-
*******************************************************************************
* Copyright (c) 2011, 2015 Diamond Light Source Ltd.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthew Gerring - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.dawnsci.remotedataset.client.dyn;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import org.eclipse.dawnsci.plotting.api.image.IPlotImageService;
import org.eclipse.dawnsci.remotedataset.ServiceHolder;
import org.eclipse.dawnsci.remotedataset.client.slice.SliceClient;
import org.eclipse.january.DatasetException;
import org.eclipse.january.dataset.DataEvent;
import org.eclipse.january.dataset.DataListenerDelegate;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
import org.eclipse.january.dataset.DatasetUtils;
import org.eclipse.january.dataset.IDataListener;
import org.eclipse.january.dataset.IDataset;
import org.eclipse.january.dataset.IDatasetChangeChecker;
import org.eclipse.january.dataset.ILazyDataset;
import org.eclipse.january.dataset.RGBDataset;
import org.eclipse.january.dataset.ShortDataset;
/**
* Used for streaming an image into the plotting system.
* @author Matthew Gerring
*
*/
class DynamicImage implements IDynamicMonitorDatasetHolder {
private boolean dynamicShape=true;
private int[] transShape;
private int[] maxShape;
private Dataset dataset;
private Thread imageMonitor;
private boolean greyScale;
private DataListenerDelegate delegate;
private SliceClient<BufferedImage> client;
/**
* @param isRGB
* @param client the client used to create the connection, for instance MJPG
* @param shape the shape of the data if known, or do not set it if not.
*/
public DynamicImage(boolean isRGB, SliceClient<BufferedImage> client, int... shape) {
greyScale = !isRGB;
dataset = greyScale ? DatasetFactory.zeros(ShortDataset.class, shape) : DatasetFactory.zeros(RGBDataset.class, shape);
delegate = new DataListenerDelegate();
this.client = client;
}
/**
* Starts notifying the IDataListener's with the current
* thread, blocking until there are no more images.
*
* @throws Exception
*/
public void start() throws Exception {
start(-1);
}
/**
* Starts notifying the IDataListener's with the current
* thread, blocking until there are no more images or if
* maxImages>0 until that number of images has been received.
*
* @throws Exception
*/
public void start(int maxImages) throws Exception {
int count = 0;
while(!client.isFinished()) {
final BufferedImage image = client.take();
if (image==null) break;
IPlotImageService plotImageService = ServiceHolder.getPlotImageService();
if (plotImageService == null) {
throw new NullPointerException("Plot image service not set");
}
Dataset im = DatasetUtils.convertToDataset(plotImageService.createDataset(image));
if (greyScale && im instanceof RGBDataset) im = ((RGBDataset)im).getRedView();
setDataset(im);
delegate.fire(new DataEvent(im.getName(), im.getShape()));
++count;
if (count>maxImages && maxImages>-1) return;
if (client.getSleep()>-1) {
delay(client.getSleep());
}
}
}
public static void delay(long waitTimeMillis) throws InterruptedException {
Thread.sleep(waitTimeMillis);
}
@Override
public void setDataset(IDataset sdata) {
Serializable buffer = DatasetUtils.cast(greyScale ? ShortDataset.class : RGBDataset.class, sdata).getBuffer();
int[] shape = sdata.getShape();
if (dynamicShape) {
dataset.overrideInternal(buffer, shape);
} else {
dataset.overrideInternal(buffer, null);
this.transShape = shape;
}
}
@Override
public ILazyDataset getDataset() {
return dataset;
}
@Override
public boolean resize(int... newShape) {
return true;
}
public void setShapeDynamic(boolean isDyn) {
dynamicShape = isDyn;
if (dynamicShape && transShape!=null) {
dataset.overrideInternal(null, transShape);
transShape = null;
}
}
@Override
public void addDataListener(IDataListener l) {
delegate.addDataListener(l);
}
@Override
public void removeDataListener(IDataListener l) {
delegate.removeDataListener(l);
}
@Override
public void fireDataListeners() {
// TODO add method to DataConnection
}
public int[] getMaxShape() {
if (maxShape==null) return dataset.getShape();
return maxShape;
}
public void setMaxShape(int... maxShape) {
this.maxShape = maxShape;
}
@Override
public void startUpdateChecker(int milliseconds, IDatasetChangeChecker checker) {
throw new IllegalArgumentException("Method not implemented. Use connect() instread!");
}
@Override
public String getPath() {
return client.getPath();
}
@Override
public void setPath(String path) {
client.setPath(path);
}
@Override
public String getDatasetName() {
return client.getDataset();
}
@Override
public void setDatasetName(String dataset) {
client.setDataset(dataset);
}
@Override
public String connect() throws DatasetException {
return connect(500, TimeUnit.MILLISECONDS);
}
@Override
public String connect(long time, TimeUnit unit) throws DatasetException {
if (imageMonitor!=null) throw new DatasetException("Cannot reconnect to already running dataset!");
// Might be a bit overkill for this task
final BlockingQueue<Exception> queue = new LinkedBlockingDeque<Exception>(1);
this.imageMonitor = new Thread(new Runnable() {
public void run() {
try {
start(); // Just keep going until we are interrupted...
} catch (Exception e) {
queue.add(e);
}
}
});
imageMonitor.setName("Monitor "+ dataset.getName());
imageMonitor.setDaemon(true);
imageMonitor.setPriority(Thread.MIN_PRIORITY); // TODO Is that right?
imageMonitor.start();
Exception e = null;
try {
e = queue.poll(time, unit);
} catch (InterruptedException e1) {
e = e1;
}
if (e!=null) throw new DatasetException(e);
return imageMonitor.getName(); // So that you can know if the runner is going.
}
@Override
public void disconnect() throws DatasetException {
client.setFinished(true);
imageMonitor = null;
}
@Override
public boolean refreshShape() {
return true;
}
public boolean isWritingExpected() {
return client.isWritingExpected();
}
public void setWritingExpected(boolean writingExpected) {
client.setWritingExpected(writingExpected);
}
}