/*******************************************************************************
* Copyright (c) 2009 University of Edinburgh.
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the BSD Licence, which accompanies this feature
* and can be downloaded from http://groups.inf.ed.ac.uk/pepa/update/licence.txt
******************************************************************************/
package uk.ac.ed.inf.biopepa.ui.wizards.timeseries;
import java.io.ByteArrayInputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.swt.widgets.Display;
import uk.ac.ed.inf.biopepa.core.BioPEPAException;
import uk.ac.ed.inf.biopepa.core.compiler.ModelCompiler;
import uk.ac.ed.inf.biopepa.core.interfaces.Result;
import uk.ac.ed.inf.biopepa.core.interfaces.Solver;
import uk.ac.ed.inf.biopepa.core.sba.ExperimentSet;
import uk.ac.ed.inf.biopepa.core.sba.Parameters;
import uk.ac.ed.inf.biopepa.core.sba.PhaseLine;
import uk.ac.ed.inf.biopepa.core.sba.SBAModel;
import uk.ac.ed.inf.biopepa.core.sba.ExperimentLine;
import uk.ac.ed.inf.biopepa.ui.BioPEPAPlugin;
import uk.ac.ed.inf.biopepa.ui.interfaces.BioPEPAModel;
import uk.ac.ed.inf.common.ui.plotting.*;
import uk.ac.ed.inf.common.ui.plotting.data.InfoWithAxes;
import uk.ac.ed.inf.common.ui.plotting.data.Series;
import uk.ac.ed.inf.common.ui.plotview.PlotViewPlugin;
/**
*
* @author ajduguid
*
*/
public class AnalysisJob extends Job {
private static String term = System.getProperty("line.separator");
BioPEPAModel model;
Solver solver;
Parameters parameters;
ExperimentSet experimentation;
private boolean justBuildCsv;
private IPath csvPath;
private PhaseLine[] phaseLines;
AnalysisJob(BioPEPAModel model, Parameters parameters,
ExperimentSet experiments, Solver solver) {
super("Time-series Analysis (" + solver.getShortName() + ")");
this.model = model;
this.parameters = parameters;
this.solver = solver;
this.experimentation = experiments;
// default setting is to draw the graph.
this.justBuildCsv = false;
this.csvPath = null;
}
public void setPhaseLines(PhaseLine[] lines){
this.phaseLines = lines;
}
public void setJustBuildCsv(boolean b) {
this.justBuildCsv = b;
}
public void setCsvPath(IPath p) {
this.csvPath = p;
}
@Override
protected IStatus run(IProgressMonitor monitor) {
LinkedList<ExperimentLine> experimentLines = experimentation.getExperimentLines();
try {
for (ExperimentLine experLine : experimentLines) {
if (experLine.getResult() == null){
model.overrideAndRecompile(experLine);
Result result;
if (phaseLines == null){
result = model.timeSeriesAnalysis(solver,
parameters,
monitor);
} else {
result = model.runPhasesTimeSeries(solver,
parameters,
monitor,
phaseLines);
}
experLine.setResult(result);
}
}
} catch (final CoreException e) {
return e.getStatus();
} catch (BioPEPAException e){
return new Status(IStatus.ERROR, BioPEPAPlugin.PLUGIN_ID, e.getMessage());
}
try {
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
// Now we draw the graphs, either separately or
// as a single graph.
if (experimentation.getSeparateGraphs()) {
for (ExperimentLine experLine : experimentLines) {
LinkedList<ExperimentLine> singleList = new LinkedList<ExperimentLine>();
singleList.add(experLine);
drawGraph(false, experLine.getName(), singleList);
}
} else {
drawGraph(true, "results", experimentLines);
}
} catch (Exception e) {
return new Status(IStatus.ERROR, BioPEPAPlugin.PLUGIN_ID, e.getMessage());
}
return Status.OK_STATUS;
}
// Draws a group of results to the same graph
// If you are drawing a single result, then you probably want both
// the variable string and the single entry string to be ""
private void drawGraph(boolean isSingleGraph, String graphTitle,
LinkedList<ExperimentLine> experimentLines) {
// Maybe I could think about throwing an exception or something.
if (experimentLines.isEmpty()) {
return;
}
/*
* It would be nice to avoid doing this in the common case that all
* have been created with the same algorithm and hence all the results
* are normalised anyway.
*/
double [] normalisedTimePoints = experimentLines.get(0).getResult().getTimePoints();
for (int resultIndex = 1; resultIndex < experimentLines.size(); resultIndex++){
Result result = experimentLines.get(resultIndex).getResult();
result.normaliseResult(normalisedTimePoints);
}
// Collect the y information for the graph.
InfoWithAxes info = new InfoWithAxes();
// info.setXSeries(Series.create(results[0].getTimePoints(), "Time"));
List<Series> list = info.getYSeries();
for (ExperimentLine experLine : experimentLines) {
Result result = experLine.getResult();
info.setXSeries(Series.create(result.getTimePoints(), "Time"));
String[] names = result.getComponentNames();
String name = experLine.getName();
String prefix = (isSingleGraph && name != null) ? (name + " - ") : "";
for (int i = 0; i < names.length; i++) {
list.add(Series.create(result.getTimeSeries(i), prefix + names[i]));
}
}
info.setShowLegend(true);
info.setYLabel("Process Count / Variable Value");
IPath path = model.getUnderlyingResource().getFullPath();
path.removeFileExtension();
/*
* Set the graph title to essentially the name of the biopepa file, if
* the graph title given is not empty then we appent that onto the end
* of the biopepa file name.
*/
if (graphTitle.equals("")) {
info.setGraphTitle(path.lastSegment());
} else {
info.setGraphTitle(path.lastSegment() + " - " + graphTitle);
}
final IChart chart = Plotting.getPlottingTools().createTimeSeriesChart(info);
Result[] resultsArray = new Result[experimentLines.size()];
for (int i = 0; i < experimentLines.size(); i++) {
resultsArray[i] = experimentLines.get(i).getResult();
}
ResultsAdapter ra = new ResultsAdapter(resultsArray);
chart.setSemanticElement(ra);
// Now if we are writing out to a csv file
// then we do so now, the path will depend on whether
// we are to attach the prefix or not.
// System.out.println ("Graph title is: " + graphTitle);
if (csvPath != null) {
IPath thisCsvPath = (IPath) csvPath.clone();
if (!isSingleGraph) {
thisCsvPath = thisCsvPath.removeFileExtension();
String lastBit = thisCsvPath.lastSegment();
thisCsvPath = thisCsvPath.removeLastSegments(1);
String gTitle = graphTitle.replace('.', '_');
thisCsvPath = thisCsvPath.append(lastBit + "_" + gTitle);
// thisCsvPath = thisCsvPath.append("_" + graphTitle);
// thisCsvPath = thisCsvPath.addFileExtension(graphTitle);
thisCsvPath = thisCsvPath.addFileExtension("csv");
}
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(thisCsvPath);
try {
ByteArrayInputStream is = new ByteArrayInputStream(Plotting.getPlottingTools().convertToCSV(chart));
if (file.exists()) {
file.setContents(is, true, false, new NullProgressMonitor());
} else {
file.create(is, true, new NullProgressMonitor());
}
// should open the file
// org.eclipse.ui.ide.IDE.openEditor(this.view.getPage(), file);
} catch (Exception e) {
System.out.println(e.getMessage());
/*
* ErrorDialog.openError(shell, "Error converting",
* "An error has occurred while converting the resource",
* PlotViewPlugin.wrapException("Error converting chart", e));
*/
}
}
if (!justBuildCsv) {
Runnable runnable = new Runnable() {
public void run() {
PlotViewPlugin.getDefault().reveal(chart);
}
};
Display.getDefault().syncExec(runnable);
}
}
private class ResultsAdapter implements ISemanticElement {
private Result[] results;
ResultsAdapter(Result[] results) {
this.results = results;
}
@SuppressWarnings("rawtypes")
public Object getAdapter(Class adapter) {
if (adapter.equals(Result.class))
return results;
return null;
}
public String getDescription(String format) {
StringBuilder sb = new StringBuilder();
if (ISemanticElement.CSV_FORMAT.equals(format)) {
for (Result result : results) {
sb.append("# Simulator: ").append(result.getSimulatorName()).append(term);
for (Entry<String, Number> me : result.getSimulatorParameters().entrySet())
sb.append("# ").append(me.getKey()).append(": ").append(me.getValue().toString()).append(term);
sb.append("# Model Parameters").append(term);
for (Entry<String, Number> me : result.getModelParameters().entrySet())
sb.append("# ").append(me.getKey()).append(" = ").append(me.getValue().toString()).append(term);
// sb.delete((sb.length() - term.length()), sb.length());
sb.append("# Run time = " + result.getSimulationRunTime()).append(term);
}
}
return sb.toString();
}
}
}