package de.mpicbg.knime.scripting.matlab.plots;
import de.mpicbg.knime.scripting.core.FlowVarUtils;
import de.mpicbg.knime.scripting.core.exceptions.KnimeScriptingException;
import de.mpicbg.knime.scripting.matlab.AbstractMatlabScriptingNodeModel;
import de.mpicbg.knime.scripting.matlab.ctrl.MatlabCode;
import de.mpicbg.knime.scripting.matlab.ctrl.MatlabFileTransfer;
import de.mpicbg.knime.scripting.matlab.ctrl.MatlabTable;
import org.knime.core.data.image.png.PNGImageContent;
import org.knime.core.node.*;
import org.knime.core.node.defaultnodesettings.SettingsModelBoolean;
import org.knime.core.node.defaultnodesettings.SettingsModelInteger;
import org.knime.core.node.defaultnodesettings.SettingsModelIntegerBounded;
import org.knime.core.node.defaultnodesettings.SettingsModelString;
import org.knime.core.node.port.PortObject;
import org.knime.core.node.port.PortObjectSpec;
import org.knime.core.node.port.PortType;
import org.knime.core.node.port.image.ImagePortObject;
import org.knime.core.node.port.image.ImagePortObjectSpec;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Node model for the MATLAB plot.
*
* @author Tom Haux, Felix Meyenhofer
*/
public class MatlabPlotNodeModel extends AbstractMatlabScriptingNodeModel {
/** Setting name for the plot width */
private static final String FIGURE_WIDTH_SETTING_NAME = "figure.width";
/** Setting name for the plot height */
private static final String FIGURE_HEIGHT_SETTING_NAME = "figure.height";
/** Setting name for the plot output file path */
private static final String OUTPUT_FILE_SETTING_NAME = "figure.output.file";
/** Setting name for the plot file overwrite option */
private static final String OVERWRITE_SETTING_NAME = "overwrite.ok";
/** Date string for the output file name generation */
private static String TODAY = new SimpleDateFormat("yyMMdd").format(new Date(System.currentTimeMillis()));
/** Default image port type */
protected static final ImagePortObjectSpec IM_PORT_SPEC = new ImagePortObjectSpec(PNGImageContent.TYPE);
/** Default plot width */
private static final int PLOT_DEFAULT_WIDTH = 800;
/** Default plot height */
private static final int PLOT_DEFAULT_HEIGHT = 600;
/** MATLAB plot image holder */
private Image image;
/**
* Default Constructor
*/
public MatlabPlotNodeModel() {
this(createPorts(1), new PortType[]{ImagePortObject.TYPE});
}
/**
* Constructor imposing the input port number
*
* @param inPorts
*/
public MatlabPlotNodeModel(PortType[] inPorts) {
this(inPorts, new PortType[]{ImagePortObject.TYPE});
}
/**
* Constructor defining input and output ports
*
* @param inPorts
* @param outports
*/
public MatlabPlotNodeModel(PortType[] inPorts, PortType[] outports) {
super(inPorts, outports);
addModelSetting(FIGURE_HEIGHT_SETTING_NAME, createPropFigureHeightSetting());
addModelSetting(FIGURE_WIDTH_SETTING_NAME, createPropFigureWidthSetting());
addModelSetting(OUTPUT_FILE_SETTING_NAME, createPropOutputFileSetting());
}
/**
* {@inheritDoc}
*/
@Override
protected PortObjectSpec[] configure(final PortObjectSpec[] inSpecs) throws InvalidSettingsException {
super.configure(inSpecs);
return new PortObjectSpec[]{IM_PORT_SPEC};
}
/**
* {@inheritDoc}
*/
@Override
public String getDefaultScript(String defaultScript) {
return super.getDefaultScript(AbstractMatlabScriptingNodeModel.DEFAULT_PLOTCMD);
}
/**
* {@inheritDoc}
*/
@Override
protected void saveInternals(File nodeDir, ExecutionMonitor executionMonitor) throws IOException, CanceledExecutionException {
if (image != null) {
File imageFile = new File(nodeDir, "image.bin");
FileOutputStream f_out = new FileOutputStream(imageFile);
// Write object with ObjectOutputStream
ObjectOutputStream obj_out = new ObjectOutputStream(new BufferedOutputStream(f_out));
// Write object out to disk
obj_out.writeObject(new ImageIcon(image));
obj_out.close();
}
}
/**
* {@inheritDoc}
*/
@Override
protected void loadInternals(File nodeDir, ExecutionMonitor executionMonitor) throws IOException, CanceledExecutionException {
super.loadInternals(nodeDir, executionMonitor);
try {
File nodeImageFile = new File(nodeDir, "image.bin");
FileInputStream f_in = new FileInputStream(nodeImageFile);
// Read object using ObjectInputStream
ObjectInputStream obj_in = new ObjectInputStream(new BufferedInputStream(f_in));
// Read an object
image = ((ImageIcon) obj_in.readObject()).getImage();
obj_in.close();
} catch (Throwable ignored) {
}
}
/**
* Getter for the plot height (from node dialog settings)
*
* @return Plot height
*/
protected int getDefHeight() {
return ((SettingsModelInteger)getModelSetting(FIGURE_HEIGHT_SETTING_NAME)).getIntValue();//propHeight.getIntValue();
}
/**
* Getter for the plot width (from the node settings)
* @return Plot width
*/
protected int getDefWidth() {
return ((SettingsModelInteger)getModelSetting(FIGURE_WIDTH_SETTING_NAME)).getIntValue();//propWidth.getIntValue();
}
/**
* Getter for the plot image output file path (from node dialog settings)
*
* @return Plot image file path
*/
private String getOutputFilePath() {
return ((SettingsModelString)getModelSetting(OUTPUT_FILE_SETTING_NAME)).getStringValue();
}
/**
* Getter for the output image overwrite flag (from the node dialog settings
*
* @return Overwrite flag
*/
private boolean getOverwriteFlag() {
return ((SettingsModelBoolean)getModelSetting(OVERWRITE_SETTING_NAME)).getBooleanValue();
}
/**
* Getter for the plot image
*
* @return Plot image object
*/
public Image getImage() {
return image;
}
/**
* Generate an plot image output file name
*
* @return File name
*/
private String prepareOutputFileName() {
String fileName = getOutputFilePath();
// process flow-variables
fileName = FlowVarUtils.replaceFlowVars(fileName, this);
// 1) date
fileName = fileName.replace("$$DATE$$", TODAY);
// 2) user
fileName = fileName.replace("$$USER$$", System.getProperty("user.name"));
// 3) workspace dir
if (fileName.contains("$$WS$$")) {
String wsLocation = getFlowVariable("knime.workspace");
fileName = fileName.replace("$$WS$$", wsLocation);
}
return fileName;
}
/**
* Create the plot width setting
*
* @return Plot width setting
*/
public static SettingsModelInteger createPropFigureWidthSetting() {
return new SettingsModelIntegerBounded(FIGURE_WIDTH_SETTING_NAME, PLOT_DEFAULT_WIDTH, 100, 5000);
}
/**
* Create the plot height setting
*
* @return Plot height setting
*/
public static SettingsModelInteger createPropFigureHeightSetting() {
return new SettingsModelIntegerBounded(FIGURE_HEIGHT_SETTING_NAME , PLOT_DEFAULT_HEIGHT, 100, 5000);
}
/**
* Create the Overwrite option setting
*
* @return Overwrite setting
*/
public static SettingsModelBoolean createOverwriteFileSetting() {
return new SettingsModelBoolean(OVERWRITE_SETTING_NAME , false);
}
/**
* Create the output file path setting
*
* @return Output file path
*/
public static SettingsModelString createPropOutputFileSetting() {
return new SettingsModelString(OUTPUT_FILE_SETTING_NAME , "") {
@Override
protected void validateSettingsForModel(NodeSettingsRO settings) throws InvalidSettingsException {
}
};
}
/**
* {@inheritDoc}
*/
@Override
protected PortObject[] executeImpl(PortObject[] inData,
ExecutionContext exec) throws Exception {
PortObject[] outPorts = new PortObject[1];
try {
// Get the input table
BufferedDataTable inputTable = (BufferedDataTable)inData[0];
// Create the plot script
String snippet = prepareScript();
exec.checkCanceled();
// Prepare snippet temp-file
codeFile = new MatlabFileTransfer(AbstractMatlabScriptingNodeModel.SNIPPET_TEMP_FILE_PREFIX,
AbstractMatlabScriptingNodeModel.SNIPPET_TEMP_FILE_SUFFIX);
// Prepare the plot temp file
plotFile = new MatlabFileTransfer(AbstractMatlabScriptingNodeModel.PLOT_TEMP_FILE_PREFIX,
AbstractMatlabScriptingNodeModel.PLOT_TEMP_FILE_SUFFIX);
// Get the input table data ready for processing
table = new MatlabTable(inputTable);
if (tableTransferMethod.equals("file")) {
// Transfer the KNIME table as hash map object dump to the JVM temp-folder
table.writeHashMapToTempFolder();
// Prepare the parser temp-file
parserFile = new MatlabFileTransfer(AbstractMatlabScriptingNodeModel.MATLAB_HASHMAP_SCRIPT);
// Prepare the code snippet
code = new MatlabCode(snippet, matlabWorkspaceType,
parserFile.getPath(),
codeFile.getPath(),
table.getHashMapTempPath(),
plotFile.getPath(),
getDefWidth(), getDefHeight());
codeFile.save(new ByteArrayInputStream(code.getScript().getBytes()));
String cmd = code.getScriptExecutionCommand(codeFile.getPath(), false, false);
// Execute
matlabProxy = matlabConnector.acquireProxyFromQueue();
matlabProxy.eval(cmd);
MatlabCode.checkForScriptErrors(matlabProxy);
matlabProxy.eval(MatlabCode.getPlotNodeMessage(false));
// releaseMatlabProxy(proxy);
} else if (tableTransferMethod.equals("workspace")) {
// Get a proxy (block it)
matlabProxy = matlabConnector.acquireProxyFromQueue();
// Push the table to the input variable in the MATLAB workspace.
table.pushTable2MatlabWorkspace(matlabProxy, matlabWorkspaceType);
// Prepare the code snippet.
code = new MatlabCode(snippet, matlabWorkspaceType,
codeFile.getPath(),
plotFile.getPath(),
getDefWidth(), getDefHeight());
codeFile.save(new ByteArrayInputStream(code.getScript().getBytes()));
String cmd = code.getScriptExecutionCommand(codeFile.getPath(), true, false);
// Execute
matlabProxy.eval(cmd);
MatlabCode.checkForScriptErrors(matlabProxy);
matlabProxy.eval(MatlabCode.getPlotNodeMessage(false));
}
exec.checkCanceled();
// Fetch the image file form the server and load it
image = MatlabPlotCanvas.toBufferedImage(new ImageIcon(plotFile.getPath()).getImage());
// Prepare the image file for KNIME to display
String fileName = prepareOutputFileName();
if (!fileName.isEmpty()) {
if (!getOverwriteFlag() && new File(fileName).exists()) {
throw new RuntimeException("Overwrite file is disabled but image file '" + fileName + "' already exists");
}
ImageIO.write((BufferedImage) image, "png", new File(fileName));
}
// Create the image port object
PNGImageContent content;
FileInputStream in = new FileInputStream(plotFile.getPath());
content = new PNGImageContent(in);
in.close();
outPorts[0] = new ImagePortObject(content, IM_PORT_SPEC);
// Housekeeping
cleanup();
} catch (Exception e) {
throw e;
} finally {
if ((matlabConnector != null) && (matlabProxy != null))
matlabConnector.returnProxyToQueue(matlabProxy);
}
return outPorts;
}
/**
* {@inheritDoc}
*/
@Override
protected void openIn(PortObject[] inData, ExecutionContext exec)
throws KnimeScriptingException {
throw new KnimeScriptingException("The functionality to open data external is not yet implemented");
}
}