All Downloads are FREE. Search and download functionalities are using the official Maven repository.

decodes.tsdb.comprungui.CompRunGuiFrame Maven / Gradle / Ivy

Go to download

A collection of software for aggregatting and processing environmental data such as from NOAA GOES satellites.

The newest version!
/*
 * $Id$
 * 
 * $Log$
 * Revision 1.3  2018/11/14 15:59:56  mmaloney
 * Remove obsolete catch block.
 *
 * Revision 1.2  2018/06/04 19:23:38  mmaloney
 * HDB issue where deleted values were being displayed on table and graph.
 *
 * Revision 1.1  2017/08/22 19:57:35  mmaloney
 * Refactor
 *
 * 
 * Copyright 2014 U.S. Army Corps of Engineers, Hydrologic Engineering Center.
 */
package decodes.tsdb.comprungui;

import ilex.gui.DateTimeCalendar;
import ilex.util.TeeLogger;
import ilex.util.TextUtil;
import ilex.util.Logger;
import ilex.var.NoConversionException;
import ilex.var.TimedVariable;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ResourceBundle;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.Vector;
import java.util.concurrent.ExecutionException;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.table.AbstractTableModel;

import opendcs.dai.AlgorithmDAI;
import opendcs.dai.TimeSeriesDAI;
import opendcs.dai.TsGroupDAI;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.PeriodAxis;
import org.jfree.chart.axis.PeriodAxisLabelInfo;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.DatasetRenderingOrder;
import org.jfree.chart.plot.SeriesRenderingOrder;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.Hour;
import org.jfree.data.time.Minute;
import org.jfree.data.time.Month;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.Week;
import org.jfree.data.time.Year;
import org.jfree.data.xy.XYDataset;
import org.slf4j.LoggerFactory;

import decodes.dbeditor.TraceDialog;
import decodes.dbeditor.TraceLogger;
import decodes.gui.TopFrame;
import decodes.tsdb.BadTimeSeriesException;
import decodes.tsdb.CTimeSeries;
import decodes.tsdb.DataCollection;
import decodes.tsdb.DbCompException;
import decodes.tsdb.DbCompParm;
import decodes.tsdb.DbCompResolver;
import decodes.tsdb.DbComputation;
import decodes.tsdb.DbIoException;
import decodes.tsdb.DuplicateTimeSeriesException;
import decodes.tsdb.NoSuchObjectException;
import decodes.tsdb.TimeSeriesDb;
import decodes.tsdb.TimeSeriesIdentifier;
import decodes.tsdb.TsGroup;
import decodes.tsdb.VarFlags;
import decodes.tsdb.alarm.AlarmManager;
import decodes.tsdb.compedit.ComputationsEditPanel;
import decodes.tsdb.compedit.ComputationsListPanel;
import decodes.tsdb.groupedit.TimeSeriesSelectDialog;
import decodes.util.DecodesSettings;

/**
 * The frame for running computations interactively.
 */
@SuppressWarnings("serial")
public class CompRunGuiFrame extends TopFrame
{
	private static org.slf4j.Logger log = LoggerFactory.getLogger(CompRunGuiFrame.class);
	static ResourceBundle labels = null;
	private static ResourceBundle genericLabels = null;
	public static String description;
	public static String selectFromListLabel;
	private String removeFromListLabel;
	private String selectButtonLabel;
	private String selectTimeRun;
	private String fromLabel;
	private String toLabel;
	private String chartXLabel;
	private String runCompsButton;
	private String saveOutputButton;
	private String timeLabel;
	private String noCompSelectedErr;
	private String closeButtonLabel;// generic
	private String saveCompOutput;
	private String cancelComputationExecution;
	public static String okButtonLabel;// generic
	public static String cancelButtonLabel;// generic
	public static String dateTimeColumnLabel;
	public static String inputLabel;
	public static String outputLabel;

	private ComputationsTable mytable;
	private Vector myoutputs = new Vector<>();
	private TimeSeriesDb theDb = null;
	private DateTimeCalendar fromDTCal;
	private DateTimeCalendar toDTCal;
	private TimeSeriesCollection[] datasets;
	private JFreeChart mychart;
	private TimeSeriesTable timeSeriesTable;
	private String module = "RunComputationFrame";
	private ChartPanel chartPanel;

	private boolean standAloneMode = false;
	private boolean needToSave = false;
	private ComputationsEditPanel compEditParent;
	private String timeZoneStr;
	private RunComputationsFrameTester runCompFrametester;
	private ComputationsListDialog computationsListDialog = null;
	private JButton traceButton = new JButton("Trace Execution");
	private JProgressBar progressBar = new JProgressBar(0,100);
	private SwingWorker,CTimeSeries> compExecutionWorker = null;
	private SwingWorker,Void> buildTimeSeriesListWorker = null;
	private JButton cancelExecutionButton = null;
	private JButton runButton = null;
	JButton saveButton = null;

	private TraceDialog traceDialog = null;
	private String cancelComputationExecutionLabel;
	private ProgressState progress;
	/**
	 * Constructor
	 * 
	 * @param standAloneMode
	 *            True if running from launcher or tester. False if running
	 *            inside compedit.
	 */
	public CompRunGuiFrame(boolean standAloneMode)
	{
		super();

		this.standAloneMode = standAloneMode;

		labels = RunComputationsFrameTester.getLabels();
		genericLabels = RunComputationsFrameTester.getGenericLabels();
		timeZoneStr = DecodesSettings.instance().sqlTimeZone;
		timeZoneStr = timeZoneStr == null ? "UTC" : timeZoneStr;
		setAllLabels();
		chartXLabel = "Time";

		JPanel mycontent = (JPanel)this.getContentPane();
		mycontent.setLayout(new BoxLayout(mycontent, BoxLayout.Y_AXIS));

		this.setTitle(labels.getString("RunComputationsFrame.frameTitle"));
		this.trackChanges("runcomps");
		traceDialog = new TraceDialog(this, false);
		traceDialog.setTraceType("Computation Run");
		mycontent.add(listPanel());
		mycontent.add(timePanel());
		mycontent.add(getChart());
		mycontent.add(getTable());
		mycontent.add(closePanel());
		pack();

		// Default operation is to do nothing when user hits 'X' in
		// upper right to close the window. We will catch the closing
		// event and do the same thing as if user had hit close.
		setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
		addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				doClose();
			}
		});
		exitOnClose = true;

	}

	private void setAllLabels()
	{
		description = labels.getString("RunComputationsFrame.description");
		selectFromListLabel = labels.getString("RunComputationsFrame.selectFromListLabel");
		removeFromListLabel = labels.getString("RunComputationsFrame.removeFromListLabel");
		selectButtonLabel = labels.getString("RunComputationsFrame.selectButtonLabel");
		selectTimeRun = labels.getString("RunComputationsFrame.selectTimeRun");
		fromLabel = labels.getString("RunComputationsFrame.fromLabel");
		toLabel = labels.getString("RunComputationsFrame.toLabel");
		chartXLabel = labels.getString("RunComputationsFrame.chartXLabel");
		runCompsButton = labels.getString("RunComputationsFrame.runCompsButton");
		saveOutputButton = labels.getString("RunComputationsFrame.saveOutputButton");
		timeLabel = labels.getString("RunComputationsFrame.timeLabel");
		noCompSelectedErr = labels.getString("RunComputationsFrame.noCompSelectedErr");
		closeButtonLabel = genericLabels.getString("close");
		saveCompOutput = labels.getString("RunComputationsFrame.saveCompOutput");
		cancelComputationExecution = labels.getString("RunComputationsFrame.cancelComputationExecution");
		okButtonLabel = genericLabels.getString("OK");
		cancelButtonLabel = genericLabels.getString("cancel");
		cancelComputationExecutionLabel = cancelButtonLabel;
		dateTimeColumnLabel = labels.getString("TimeSeriesTable.dateTimeColumnLabel") + " (" + timeZoneStr
			+ ")";
		inputLabel = labels.getString("RunComputationsFrame.inputLabel");
		outputLabel = labels.getString("RunComputationsFrame.outputLabel");
	}

	/**
	 * When lauch from Comp Edit GUI need to set the parent so that we can get
	 * the DbComputation obj when the user presses Run Computation button.
	 * 
	 * @param compEdit
	 *            the parent of this frame
	 */
	public void setParent(ComputationsEditPanel compEdit)
	{
		compEditParent = compEdit;
	}

	private JPanel listPanel()
	{
		JPanel list = new JPanel();
		if (!standAloneMode)
			return list;

		list.setPreferredSize(new Dimension(600, 200));
		list.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), labels
			.getString("RunComputationsFrame.selectCompRunTitle")));
		list.setLayout(new BorderLayout());
		JScrollPane myscroll = new JScrollPane();
		list.add(myscroll, BorderLayout.CENTER);
		JPanel listButtonPanel = new JPanel();
		list.add(listButtonPanel, BorderLayout.EAST);
		listButtonPanel.setLayout(new GridBagLayout());
		JButton selectButton = new JButton(selectButtonLabel);
		JButton removeButton = new JButton(removeFromListLabel);
		selectButton.addActionListener(new java.awt.event.ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				selectButtonPressed();

			}
		});
		removeButton.addActionListener(new java.awt.event.ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				removeButtonPressed();

			}
		});
		listButtonPanel.add(selectButton, new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTH,
			GridBagConstraints.HORIZONTAL, new Insets(4, 10, 0, 10), 0, 0));
		listButtonPanel.add(removeButton, new GridBagConstraints(0, 1, 1, 1, 0, 1, GridBagConstraints.NORTH,
			GridBagConstraints.HORIZONTAL, new Insets(4, 10, 0, 10), 0, 0));
		mytable = new ComputationsTable();

		myscroll.add(mytable);
		myscroll.setViewportView(mytable);

		if (!standAloneMode)
		{
			selectButton.setEnabled(false);
			removeButton.setEnabled(false);
		}
		return list;
	}

	private JPanel timePanel()
	{
		JPanel time = new JPanel();
		time.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), selectTimeRun));
		GridBagLayout gbl_time = new GridBagLayout();
		gbl_time.columnWidths = new int[]{353, 150, 345, 0};
		gbl_time.rowHeights = new int[]{76, 0};
		gbl_time.columnWeights = new double[]{0.0, 1.0, 0.0, Double.MIN_VALUE};
		gbl_time.rowWeights = new double[]{0.0, Double.MIN_VALUE};
		time.setLayout(gbl_time);

		JPanel runhalf = new JPanel();
		runhalf.setLayout(new GridBagLayout());
		runButton = new JButton(runCompsButton);
		runButton.addActionListener(e -> runButtonPressed());

		saveButton = new JButton(saveOutputButton);
		saveButton.setEnabled(false);
		saveButton.addActionListener(e -> saveButtonPressed());

		traceButton.setEnabled(true);
		traceButton.addActionListener(e -> traceButtonPressed());

		Date tempDate = new Date();
		fromDTCal = new DateTimeCalendar(fromLabel, null, "dd MMM yyyy", timeZoneStr);
		toDTCal = new DateTimeCalendar(toLabel, tempDate, "dd MMM yyyy", timeZoneStr);
		JPanel timehalf = new JPanel();
		timehalf.setLayout(new GridBagLayout());
		timehalf.add(fromDTCal, new GridBagConstraints(0, 0, 1, 1, 0.5, 0, GridBagConstraints.CENTER,
			GridBagConstraints.HORIZONTAL, new Insets(4, 10, 4, 10), 0, 0));
		timehalf.add(new JLabel(" (" + timeZoneStr + ")"), new GridBagConstraints(1, 0, 1, 1, 0, 0,
			GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 10, 4, 10), 0, 0));
		timehalf.add(toDTCal, new GridBagConstraints(0, 1, 1, 1, 0.5, 0, GridBagConstraints.CENTER,
			GridBagConstraints.HORIZONTAL, new Insets(4, 10, 4, 10), 0, 0));
		GridBagConstraints gbc_timehalf = new GridBagConstraints();
		gbc_timehalf.anchor = GridBagConstraints.NORTHWEST;
		gbc_timehalf.insets = new Insets(0, 0, 0, 5);
		gbc_timehalf.gridx = 0;
		gbc_timehalf.gridy = 0;
		time.add(timehalf, gbc_timehalf);
		GridBagConstraints gbc_progressBar = new GridBagConstraints();
		gbc_progressBar.fill = GridBagConstraints.HORIZONTAL;
		gbc_progressBar.insets = new Insets(0, 10, 0, 10);
		gbc_progressBar.gridx = 1;
		gbc_progressBar.gridy = 0;
		time.add(progressBar, gbc_progressBar);

		runhalf.add(runButton, new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.CENTER,
			GridBagConstraints.HORIZONTAL, new Insets(4, 10, 5, 10), 0, 0));
		runhalf.add(saveButton, new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.CENTER,
			GridBagConstraints.HORIZONTAL, new Insets(4, 10, 5, 10), 0, 0));
		runhalf.add(traceButton, new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.CENTER,
			GridBagConstraints.HORIZONTAL, new Insets(4, 10, 0, 10), 0, 0));
		GridBagConstraints gbc_runhalf = new GridBagConstraints();
		gbc_runhalf.anchor = GridBagConstraints.EAST;
		gbc_runhalf.fill = GridBagConstraints.VERTICAL;
		gbc_runhalf.gridx = 2;
		gbc_runhalf.gridy = 0;
		time.add(runhalf, gbc_runhalf);
		
		cancelExecutionButton = new JButton(cancelComputationExecutionLabel);
		cancelExecutionButton.setEnabled(false);
		GridBagConstraints gbc_cancelExecutionButton = new GridBagConstraints();
		gbc_cancelExecutionButton.insets = new Insets(4, 10, 5, 10);
		gbc_cancelExecutionButton.fill = GridBagConstraints.HORIZONTAL;
		gbc_cancelExecutionButton.gridx = 1;
		gbc_cancelExecutionButton.gridy = 1;
		runhalf.add(cancelExecutionButton, gbc_cancelExecutionButton);
		cancelExecutionButton.addActionListener(e ->
		{
			if (this.compExecutionWorker != null && !this.compExecutionWorker.isDone())
			{				
				this.compExecutionWorker.cancel(true);
				this.cancelExecutionButton.setEnabled(false);
			}
			else if (this.buildTimeSeriesListWorker != null && !this.buildTimeSeriesListWorker.isDone())
			{
				this.buildTimeSeriesListWorker.cancel(true);
				this.cancelExecutionButton.setEnabled(false);
			}
		});
		return time;
	}

	protected void traceButtonPressed()
	{
		traceDialog.setVisible(true);
	}

	/** Returns the chart panel */
	public ChartPanel getChart()
	{
		mychart = ChartFactory.createTimeSeriesChart("", timeLabel, " ",// Name
																		// of
																		// the
																		// site
																		// plus
																		// timeseries
																		// +
																		// units
			null, // dateset
			true, // legend
			true, // tooltip
			false // url
			);
		this.datasets = new TimeSeriesCollection[2];

		ChartPanel mypanel = new ChartPanel(mychart);
		chartPanel = mypanel;
		mypanel.setPreferredSize(new java.awt.Dimension(600, 500));

		mypanel.setDomainZoomable(true);
		mypanel.setRangeZoomable(true);

		Border border = BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4),
			BorderFactory.createEtchedBorder());
		mypanel.setBorder(border);
		return mypanel;
	}

	/**
	 * This methods plots all data on chart.
	 * 
	 * 
	 */
	private void plotDataOnChart(Vector ctsList, int inputs)
	{
		if (ctsList == null || ctsList.isEmpty())
			return;
		CTimeSeries correctedTs = ctsList.get(0);
		// Here we get the data to plot from the
		// TimeSeries Info read from database
		// to set the Y axis (left and right)
		// we will plot the main time series plus reference points
		// reference point will go on right if units are diferent
		mychart = ChartFactory.createTimeSeriesChart("", chartXLabel, correctedTs.getUnitsAbbr(),// Main
																									// timeseries
																									// units
			null, // dataset
			true, // legend
			true, // tooltip
			false // url
			);
		this.chartPanel.setChart(mychart);
		// Check if we have data on the main correctedTs. If no data
		// do not plot anything.
		if (correctedTs.size() == 0)
			return;
		XYDataset[] xyDatasets = new XYDataset[ctsList.size()];
		HashMap dataHashMap = new HashMap();
		this.datasets = new TimeSeriesCollection[ctsList.size()];
		DecimalFormat numFormat = new DecimalFormat("###0.00");
		// We want to paint the outputs on top of the inputs, we also want the
		// inputs to start showing on the right, so we need to
		// reset the order of rendering the lines, by default is Reverse, which
		// means: the plot renders the primary dataset last (so that the
		// primary dataset overlays the secondary datasets).
		// To reverse this set it to forward. NOW if we have many inputs and
		// outputs we still have some overlay problems.
		mychart.getXYPlot().setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
		mychart.getXYPlot().setSeriesRenderingOrder(SeriesRenderingOrder.FORWARD);
		for (int i = 0; i < ctsList.size(); i++)
		{
			CTimeSeries tempTs = ctsList.get(i);
			if (tempTs.size() == 0)
				continue; // Continue if the TS has no values to plot
			TimeSeriesIdentifier tsid = tempTs.getTimeSeriesIdentifier();
			if (tsid == null)
				continue;

			String timeSeriesName;
			if (i < inputs)
				timeSeriesName = inputLabel + tsid.getUniqueString();
			else
				timeSeriesName = outputLabel + tsid.getUniqueString();

			if (correctedTs.getUnitsAbbr().equals(tempTs.getUnitsAbbr()) && i == 0)
			{
				// When eng units are the same as the
				// main eng units plot it on the left y axis, else
				// plot it on the right y axis
				xyDatasets[i] = createDataset(i, timeSeriesName + " (" + tempTs.getUnitsAbbr() + ")", tempTs,
					false);
				mychart.getXYPlot().setDataset(xyDatasets[i]); // Y left axis

				XYPlot plot = mychart.getXYPlot();
				plot.getRangeAxis().setLabel(" ( " + (tempTs.getUnitsAbbr()).toUpperCase() + " )");
				XYLineAndShapeRenderer rr = null;
				XYItemRenderer renderer = plot.getRenderer();
				SimpleDateFormat df1 = new SimpleDateFormat("ddMMMyyyy HH:mm");
				df1.setTimeZone(TimeZone.getTimeZone(timeZoneStr));
				renderer
					.setToolTipGenerator(new StandardXYToolTipGenerator("{0}: ({1}, {2})", df1, numFormat));
				renderer.setSeriesPaint(i, Color.black);// first one black
				if (renderer instanceof XYLineAndShapeRenderer)
				{
					rr = (XYLineAndShapeRenderer) renderer;
					// rr.setBaseShapesVisible(true);
					rr.setBaseShapesVisible(false);
					// rr.setShapesFilled(true);
					rr.setShapesFilled(false);
				}
				plot.setRenderer(i, rr);
			}
			else if (correctedTs.getUnitsAbbr().equals(tempTs.getUnitsAbbr()) && i > 0)
			{
				xyDatasets[0] = createDataset(0, timeSeriesName + " (" + tempTs.getUnitsAbbr() + ")", tempTs,
					true);
				mychart.getXYPlot().setDataset(0, xyDatasets[0]);
			}
			else
			{ // Y right axis, here we can add more Y right axis
				// Units are diferent that the correctedTs
				// Find out if we have this eng units in the dataHashMap
				Integer indexOfEngUnits = (Integer) dataHashMap.get(tempTs.getUnitsAbbr());
				if (indexOfEngUnits == null)
				{ // new eng units
					dataHashMap.put(tempTs.getUnitsAbbr(), i);
					xyDatasets[i] = createDataset(i, timeSeriesName + " (" + tempTs.getUnitsAbbr() + ")",
						tempTs, false);
					XYPlot plot = mychart.getXYPlot();
					NumberAxis axis2 = new NumberAxis(" ( " + (tempTs.getUnitsAbbr()).toUpperCase() + " )");
					axis2.setAutoRangeIncludesZero(false);
					plot.setDataset(i, xyDatasets[i]);
					plot.setRangeAxis(i, axis2);
					plot.mapDatasetToRangeAxis(i, i);
					XYLineAndShapeRenderer rr = null;
					XYItemRenderer renderer = new XYLineAndShapeRenderer();
					SimpleDateFormat df1 = new SimpleDateFormat("ddMMMyyyy HH:mm");
					df1.setTimeZone(TimeZone.getTimeZone(timeZoneStr));
					renderer.setToolTipGenerator(new StandardXYToolTipGenerator("{0}: ({1}, {2})", df1,
						numFormat));
					if (renderer instanceof XYLineAndShapeRenderer)
					{
						rr = (XYLineAndShapeRenderer) renderer;
						// rr.setBaseShapesVisible(true);
						rr.setBaseShapesVisible(false);
						// rr.setShapesFilled(true);
						rr.setShapesFilled(false);
					}
					plot.setRenderer(i, rr);
				}
				else
				{
					int indexForEngUnits = indexOfEngUnits.intValue();
					xyDatasets[indexForEngUnits] = createDataset(indexForEngUnits, timeSeriesName + " ("
						+ tempTs.getUnitsAbbr() + ")", tempTs, true);
					mychart.getXYPlot().setDataset(indexForEngUnits, xyDatasets[indexForEngUnits]);
				}
			}
		}
		XYPlot plot = mychart.getXYPlot();
		plot.clearRangeMarkers(); // clear all previous markers
		plot = mychart.getXYPlot();
		NumberAxis axisLeftY = (NumberAxis) plot.getRangeAxis();// gets the Y
		// axis on the
		// (left) side
		// TickUnitSource units = NumberAxis.createIntegerTickUnits();
		// axisLeftY.setStandardTickUnits(units);
		// Include the number zero on the Main Y axis (left)
		axisLeftY.setAutoRangeIncludesZero(false);
	}

	/**
	 * Create the Time Series Data set with all data read from the DB.
	 * 
	 * @param index
	 * @param timeSeriesName
	 * @param tsIn
	 * @param sameEngUnits
	 * @return
	 */
	private XYDataset createDataset(int index, String timeSeriesName, CTimeSeries tsIn, boolean sameEngUnits)
	{
		String namePlusEuAbbr = timeSeriesName;
		TimeSeries series = new TimeSeries(namePlusEuAbbr, Minute.class);
		TimeZone timezone = TimeZone.getTimeZone(timeZoneStr);
		series.clear();
		for (int i = 0; i < tsIn.size(); i++)
		{
			TimedVariable tv = tsIn.sampleAt(i);
			
			// Don't plot values flagged for deletion.
			if (tv == null || VarFlags.mustDelete(tv))
				continue;
			try
			{ // get flag and verify if value rejected, if rejected
				// do not plot it

				double value = tv.getDoubleValue();
				Date timestamp = tv.getTime();
				series.addOrUpdate(new Minute(timestamp, timezone), value);

			}
			catch (NoConversionException e)
			{
				Logger.instance().log(Logger.E_WARNING, e.toString());
			}
		}
		if (sameEngUnits)
		{ // add to a dataset already created
			this.datasets[index].addSeries(series);
			return this.datasets[index];
		}
		else
		{
			this.datasets[index] = new TimeSeriesCollection();
			this.datasets[index].addSeries(series);
		}
		return this.datasets[index];
	}

	/**
	 * Create the X axis "Domain Axis" according to the first time and last time
	 * of the CTimeSeries.
	 * 
	 * @param plot
	 * @param ts
	 */
	public void setDomainXAxisLabels(XYPlot plot, CTimeSeries ts)
	{
		if (plot == null || ts == null)
			return;
		long firstTsTime = 0;
		long lastTsTime = 0;
		long timeRange = 0;
		TimedVariable tvFrom = ts.sampleAt(0);
		if (tvFrom != null)
		{
			firstTsTime = tvFrom.getTime().getTime();
		}
		TimedVariable tvTo = ts.sampleAt(ts.size() - 1);
		if (tvTo != null)
		{
			lastTsTime = tvTo.getTime().getTime();
		}
		timeRange = lastTsTime - firstTsTime;
		// Need to verify the first time and end time of the correctedTs
		// Depending on the time range will create the Domain Axis (x)
		// Hour or Hours range
		// Day or days range
		// week range
		// month range
		// year range
		long hourRange = 3600000L;// =milliseconds //3600=minutes;
		long dayRange = 86400000L; // 3600*24=minutes; //1000 * 60 * 60 * 24
		long weekRange = 86400000L * 7;
		long monthRange = 86400000L * 31; // 3600*24*31=minutes
		long yearRange = 86400000L * 31 * 12; // 3600*24*365 = minutes;
		// 31536000000
		PeriodAxis domainPeriodAxis = new PeriodAxis("");
		// domainPeriodAxis.setAutoRangeTimePeriodClass(Hour.class);
		// domainPeriodAxis.setAutoRangeTimePeriodClass(Minute.class);
		domainPeriodAxis.setTimeZone(TimeZone.getTimeZone(timeZoneStr));

		// To create a subtitle under the x axis label
		PeriodAxisLabelInfo[] info = new PeriodAxisLabelInfo[2];
		if (timeRange <= hourRange)
		{
			info = new PeriodAxisLabelInfo[1];
			info[0] = new PeriodAxisLabelInfo(Hour.class, new SimpleDateFormat("HH:mm"),
				PeriodAxisLabelInfo.DEFAULT_INSETS, PeriodAxisLabelInfo.DEFAULT_FONT,
				PeriodAxisLabelInfo.DEFAULT_LABEL_PAINT, false, PeriodAxisLabelInfo.DEFAULT_DIVIDER_STROKE,
				PeriodAxisLabelInfo.DEFAULT_DIVIDER_PAINT);
		}
		else if (timeRange <= dayRange)
		{
			info[0] = new PeriodAxisLabelInfo(Hour.class, new SimpleDateFormat("HH"),
				PeriodAxisLabelInfo.DEFAULT_INSETS, PeriodAxisLabelInfo.DEFAULT_FONT,
				PeriodAxisLabelInfo.DEFAULT_LABEL_PAINT, false, PeriodAxisLabelInfo.DEFAULT_DIVIDER_STROKE,
				PeriodAxisLabelInfo.DEFAULT_DIVIDER_PAINT);
			info[1] = new PeriodAxisLabelInfo(Day.class, new SimpleDateFormat("ddMMMyyyy"),
				PeriodAxisLabelInfo.DEFAULT_INSETS, PeriodAxisLabelInfo.DEFAULT_FONT,
				PeriodAxisLabelInfo.DEFAULT_LABEL_PAINT, true, PeriodAxisLabelInfo.DEFAULT_DIVIDER_STROKE,
				PeriodAxisLabelInfo.DEFAULT_DIVIDER_PAINT);
		}
		else if (timeRange <= weekRange)
		{
			info[0] = new PeriodAxisLabelInfo(Hour.class, new SimpleDateFormat("HH"),
				PeriodAxisLabelInfo.DEFAULT_INSETS, PeriodAxisLabelInfo.DEFAULT_FONT,
				PeriodAxisLabelInfo.DEFAULT_LABEL_PAINT, false, PeriodAxisLabelInfo.DEFAULT_DIVIDER_STROKE,
				PeriodAxisLabelInfo.DEFAULT_DIVIDER_PAINT);
			info[1] = new PeriodAxisLabelInfo(Day.class, new SimpleDateFormat("ddMMMyyyy"),
				PeriodAxisLabelInfo.DEFAULT_INSETS, PeriodAxisLabelInfo.DEFAULT_FONT,
				PeriodAxisLabelInfo.DEFAULT_LABEL_PAINT, true, PeriodAxisLabelInfo.DEFAULT_DIVIDER_STROKE,
				PeriodAxisLabelInfo.DEFAULT_DIVIDER_PAINT);
		}
		else if (timeRange <= monthRange)
		{
			info[0] = new PeriodAxisLabelInfo(Day.class, new SimpleDateFormat("d"),
				PeriodAxisLabelInfo.DEFAULT_INSETS, PeriodAxisLabelInfo.DEFAULT_FONT,
				PeriodAxisLabelInfo.DEFAULT_LABEL_PAINT, false, PeriodAxisLabelInfo.DEFAULT_DIVIDER_STROKE,
				PeriodAxisLabelInfo.DEFAULT_DIVIDER_PAINT);
			info[1] = new PeriodAxisLabelInfo(Month.class, new SimpleDateFormat("MMMyyyy"),
				PeriodAxisLabelInfo.DEFAULT_INSETS, PeriodAxisLabelInfo.DEFAULT_FONT,
				PeriodAxisLabelInfo.DEFAULT_LABEL_PAINT, true, PeriodAxisLabelInfo.DEFAULT_DIVIDER_STROKE,
				PeriodAxisLabelInfo.DEFAULT_DIVIDER_PAINT);
		}
		else if (timeRange <= yearRange)
		{
			info[0] = new PeriodAxisLabelInfo(Week.class, new SimpleDateFormat("W"),
				PeriodAxisLabelInfo.DEFAULT_INSETS, PeriodAxisLabelInfo.DEFAULT_FONT,
				PeriodAxisLabelInfo.DEFAULT_LABEL_PAINT, false, PeriodAxisLabelInfo.DEFAULT_DIVIDER_STROKE,
				PeriodAxisLabelInfo.DEFAULT_DIVIDER_PAINT);
			info[1] = new PeriodAxisLabelInfo(Month.class, new SimpleDateFormat("MMMyyyy"),
				PeriodAxisLabelInfo.DEFAULT_INSETS, PeriodAxisLabelInfo.DEFAULT_FONT,
				PeriodAxisLabelInfo.DEFAULT_LABEL_PAINT, true, PeriodAxisLabelInfo.DEFAULT_DIVIDER_STROKE,
				PeriodAxisLabelInfo.DEFAULT_DIVIDER_PAINT);
		}
		else
		{
			info[0] = new PeriodAxisLabelInfo(Month.class, new SimpleDateFormat("MMM"),
				PeriodAxisLabelInfo.DEFAULT_INSETS, PeriodAxisLabelInfo.DEFAULT_FONT,
				PeriodAxisLabelInfo.DEFAULT_LABEL_PAINT, false, PeriodAxisLabelInfo.DEFAULT_DIVIDER_STROKE,
				PeriodAxisLabelInfo.DEFAULT_DIVIDER_PAINT);
			info[1] = new PeriodAxisLabelInfo(Year.class, new SimpleDateFormat("yyyy"),
				PeriodAxisLabelInfo.DEFAULT_INSETS, PeriodAxisLabelInfo.DEFAULT_FONT,
				PeriodAxisLabelInfo.DEFAULT_LABEL_PAINT, true, PeriodAxisLabelInfo.DEFAULT_DIVIDER_STROKE,
				PeriodAxisLabelInfo.DEFAULT_DIVIDER_PAINT);
		}
		domainPeriodAxis.setLabelInfo(info);
		plot.setDomainAxis(domainPeriodAxis);
	}

	/** Sets the Database used here */
	public void setDb(TimeSeriesDb mydb)
	{
		this.theDb = mydb;
		timeSeriesTable.setTsdb(mydb);
	}

	private void runButtonPressed()
	{
		// Create a trace logger and put in the pipeline with tee logger.
		Logger originalLogger = Logger.instance();
		final TraceLogger traceLogger = new TraceLogger(originalLogger.getProcName());
		final TeeLogger teeLogger = new TeeLogger(originalLogger.getProcName(), originalLogger, traceLogger);
		traceLogger.setMinLogPriority(Logger.E_DEBUG3);
		Logger.setLogger(teeLogger);

		runButton.setEnabled(false);
		traceDialog.clear();
		traceLogger.setDialog(traceDialog);
		AlarmManager.deleteInstance();
		
		if (fromDTCal.getDate() == null || toDTCal.getDate() == null)
		{
			return;
		}
		final Vector inputs = new Vector();
		final Vector compVector = new Vector();

		if (!standAloneMode)
		{ // Get computation Obj from ComputationsEditPanel
			DbComputation dbComp = compEditParent.getEditedDbComputation();
			if (dbComp != null)
				compVector.add(dbComp);
		}
		else
		{
			compVector.addAll(mytable.mymodel.myvector);
		}
		if (compVector.size() == 0)
		{
			showError(noCompSelectedErr);
			return;
		}

		ifSelectionValid(compVector, (comps,compGroup) ->
			{
			progressBar.setStringPainted(true);
			progressBar.setValue(0);
			buildTimeSeriesListWorker = new SwingWorker,Void>()
			{

				@Override
				protected Vector doInBackground() throws Exception
				{
					if (!buildComputationList(compVector, compGroup, (progress) -> setProgress(progress), () -> isCancelled()))
					{
						return null;
					}
					else
					{
						return compVector;
					}
				}

				@Override
				public void done()
				{
					try
					{
						if (this.isCancelled())
						{
							runButton.setEnabled(true);
							return;
						}
						setProgress(100);
						Vector theComps = this.get();
						if (theComps != null)
						{
							runSelectedComps(theComps, originalLogger, traceLogger, inputs);
						}
						else
						{
							runButton.setEnabled(true);
						}
					}
					catch (InterruptedException | ExecutionException ex)
					{
						runButton.setEnabled(true);
						log.atError()
						.setCause(ex)
						.log("Unable to execute computations.");
						showError(ex.getLocalizedMessage());
					}
				}
			};
			buildTimeSeriesListWorker.addPropertyChangeListener(event -> updateProgress(event));
			buildTimeSeriesListWorker.execute();
		});
	}

	/**
	 * Determines if the user selection is valid, if so pass on to next step, otherwise just return.
	 * @param compVector
	 * @param actionIfValid contains the code to run if the selection is valid.
	 */
	private void ifSelectionValid(Collection compVector, BiConsumer,TsGroup> actionIfValid)
	{
		String groupCompName = null;
		TsGroup compGroup = null;
		for (DbComputation comp : compVector)
		{
			if (comp.hasGroupInput())
			{
				Logger.instance().debug3("comp " + comp.getName() + " has group input. ");
				if (compGroup == null)
				{
					compGroup = comp.getGroup();
					groupCompName = comp.getName();
					Logger.instance().debug3(
						"Will execute with group " + groupCompName + ", id=" + comp.getGroupId());
				}
				else if (compGroup.getGroupId() != comp.getGroupId())
				{
					showError("Multiple computations that are triggered from "
						+ "different time-series groups cannot be run together "
						+ "in the GUI: computation '" + groupCompName + "' uses group '"
						+ compGroup.getGroupName() + "', computation '" + comp.getName() + "' uses group '"
						+ comp.getGroupName()
						+ "'. -- Please de-select one of these computations to proceed.");
					compGroup = new TsGroup();
					compGroup.clear();
					break;
				}
				else
				{
					Logger.instance().debug3(
						"comp " + comp.getName() + " also uses group id=" + comp.getGroupId());
				}
			}
		}
		if (compGroup == null || (compGroup.getGroupName() != null))
		{
			actionIfValid.accept(compVector, compGroup);
		}
		else
		{
			log.trace("Requested computations use a different group. Unable to process.");
		}
	}

	/**
	 * Generates a list of fully concrete (algorithm + all inputs/outputs fully expands)
	 * @param compVector vector instance that will hold the generated computations.
	 * @param compGroup The Timeseries Group getting used for this computation. Maybe null to indicate a group comp
	 *                  isn't being run and inputs and outputs are already defined.
	 * @param setProgress Function that accepts the current progress of timeseries mutation
	 * @return true if the expansion of the compVector was successful.
	 */
	private boolean buildComputationList(Vector compVector, TsGroup compGroup,
										 Consumer setProgress, Supplier checkCancelled)
	{
		ArrayList groupTsIds = new ArrayList();
		if (compGroup != null)
		{
			try
			{
				cancelExecutionButton.setEnabled(true);
				DbComputation comp = compVector.get(0);
				DbCompParm firstInput = null;
				for (Iterator pit = comp.getParms(); pit.hasNext();)
				{
					DbCompParm parm = pit.next();
					if (parm.isInput())
					{
						firstInput = parm;
						break;
					}
				}
				if (firstInput == null)
				{
					showError("The computation has no inputs!");
					return false;
				}

				// Read a fresh copy of the group from the DB
				
				try (TsGroupDAI groupDAO = theDb.makeTsGroupDAO())
				{
					compGroup = groupDAO.getTsGroupById(compGroup.getGroupId());
				}

				// Expand the group and then filter it by the 1st input parm.
				ArrayList tsids = theDb.expandTsGroup(compGroup);
				progress = new ProgressState(tsids.size());
				// Collect the transformed tsids in a hash set to remove any
				// duplicates
				// that may result by transformation.
				final TreeSet transformedTsids = new TreeSet();
				final DbCompParm theFirstInput = firstInput;
				try (TimeSeriesDAI txDAI = theDb.makeTimeSeriesDAO())
				{
					txDAI.inTransaction(dao ->
					{
						try (TimeSeriesDAI tsDAI = theDb.makeTimeSeriesDAO())
						{
							tsDAI.inTransactionOf(dao);
							for (Iterator tsidit = tsids.iterator(); tsidit.hasNext();)
							{
								if (checkCancelled.get() == true)
								{
									throw new Exception("stop");
								}
								TimeSeriesIdentifier tsid = tsidit.next();

								// Transform the tsid by the parm. If the result is the same
								// as the original, that means that the parm 'matches'.
								TimeSeriesIdentifier transformed;
								progress.incDone();
								setProgress.accept(progress.getPercentDone());
								try
								{
									transformed = theDb.transformTsidByCompParm(tsDAI, tsid, theFirstInput, false, false, "");
								}
								catch (NoSuchObjectException e)
								{
									tsidit.remove();
									continue;
								}
								catch (BadTimeSeriesException e)
								{
									// Can't happen because create flag is false.
									continue;
								}
								if (transformed == null)
								{
									tsidit.remove();
									continue;
								}
								Logger.instance().debug3("   original='" + tsid.getUniqueString() + "'");
								Logger.instance().debug3("transformed='" + transformed.getUniqueString() + "'");

								transformedTsids.add(transformed);
								Logger.instance().debug3(
									"transformedTsids now has " + transformedTsids.size() + " members.");
							}
						}
					});
				}
				catch (Exception ex)
				{
					if ("stop".equals(ex.getMessage()))
					{
						return false;
					}
					throw new DbIoException("Error processing timeseries group.", ex);
				}

				TimeSeriesSelectDialog dlg = new TimeSeriesSelectDialog(theDb, false, this);
				dlg.setMultipleSelection(true);
				Logger.instance().debug3(
					"Setting selection list with " + transformedTsids.size() + " entries.");
				dlg.setTimeSeriesList(transformedTsids);
				dlg.setTitle("Select time series for Execution.");
				launchDialog(dlg);
				TimeSeriesIdentifier[] tsidarray = dlg.getSelectedDataDescriptors();
				for (TimeSeriesIdentifier tsid : tsidarray)
				{
					groupTsIds.add(tsid);
				}

				Logger.instance().debug3("Will execute with the following time-series: ");
				for (TimeSeriesIdentifier tsid : groupTsIds)
				{
					Logger.instance().debug3("   " + tsid.getUniqueString());
				}

				ArrayList concreteGroupComps = new ArrayList();
				StringBuilder errorMsgs = new StringBuilder();
				for (Iterator compit = compVector.iterator(); compit.hasNext();)
				{
					comp = compit.next();
					if (!comp.hasGroupInput())
					{
						continue;
					}

					// This is a group computation. Use resolver to make
					// a concrete copy, and then add it to concreteGroupComps.
					// First call compit.remote() to remove the abstract copy.
					compit.remove();

					for (TimeSeriesIdentifier tsid : groupTsIds)
					{
						try
						{
							DbComputation concrete = DbCompResolver.makeConcrete(theDb, tsid, comp, true);
							concreteGroupComps.add(concrete);
						}
						catch (NoSuchObjectException ex)
						{
							errorMsgs.append("Cannot execute '" + comp.getName() + "' for time series '"
								+ tsid.getUniqueString() + "': " + ex.getMessage() + "\r\n");
						}
					}
				}
				compVector.addAll(concreteGroupComps);
			}
			catch (DbIoException ex)
			{
				showError("Cannot expand group '" + compGroup.getGroupName() + "': " + ex);
				return false;
			}
		}
		return true;
	}

	/**
	 * Actually run the selected computation with the given inputs.
	 * @param compVector list of computations that are fully defined. E.g. it is explicit *what* timeseries to use for input and output.
	 * @param originalLogger
	 * @param traceLogger
	 * @param inputs valid vector that is used to add input timeseries for computations that are actually run.
	 */
	private void runSelectedComps(Collection compVector, Logger originalLogger, TraceLogger traceLogger, Vector inputs)
	{
		final Vector both = new Vector();
		// Flush the text area inside trace dialog
		// Create the trace logger here and put in pipe with tee logger.
		// Put trace dialog reference in trace logger.
		myoutputs.clear();
		compExecutionWorker = new SwingWorker,CTimeSeries>() {
			@Override
			public List doInBackground()
			{
				runButton.setEnabled(false);
				Vector outputs = new Vector();
				progress = new ProgressState(compVector.size());
				for (DbComputation comp : compVector)
				{
					if(this.isCancelled())
					{
						return outputs;
					}
					DataCollection runme = new DataCollection();
					// ArrayList outputIDs = new ArrayList();
					ArrayList outputParms = new ArrayList();

					boolean lowerBoundClosed = true;
					boolean upperBoundClosed = false;
					lowerBoundClosed = TextUtil.str2boolean(comp.getProperty("aggLowerBoundClosed"));
					upperBoundClosed = TextUtil.str2boolean(comp.getProperty("aggUpperBoundClosed"));

					// Get all inputs and expand them
					for (Iterator parmIt = comp.getParms(); parmIt.hasNext();)
					{
						DbCompParm parm = parmIt.next();

						// check for null on parm.getAlgoParmType
						if (parm.isInput() && parm.getSiteDataTypeId() != null && !parm.getSiteDataTypeId().isNull())
						{

							CTimeSeries ts = new CTimeSeries(parm);
							ts.setModelRunId(comp.getModelRunId());
							TimeSeriesDAI timeSeriesDAO = theDb.makeTimeSeriesDAO();
							try
							{
								timeSeriesDAO.fillTimeSeries(ts, fromDTCal.getDate(), toDTCal.getDate(),
									lowerBoundClosed, upperBoundClosed, false);
							}
							catch (Exception ex)
							{
								String msg = module + " Exception filling input timeseries in "
									+ "runButtonPressed() " + ex;
								if (!isCancelled())
								{
									showError(msg);
								}
								Logger.instance().warning(msg);
								StringWriter sw = new StringWriter();
								PrintWriter pw = new PrintWriter(sw);
								ex.printStackTrace(pw);
								Logger.instance().warning(sw.toString());
								continue;
							}
							finally
							{
								timeSeriesDAO.close();
							}
							for (int pos = 0; pos < ts.size(); pos++)
								VarFlags.setWasAdded(ts.sampleAt(pos));

							try
							{
								runme.addTimeSeries(ts);
								inputs.add(ts);
							}
							catch (DuplicateTimeSeriesException e)
							{
								// MJM some comps, like monthly delta may
								// Have the same TS as two separate inputs.
							}
						}
						else if (parm.isOutput())
						{
							// Record all the outputs to be sorted later
							outputParms.add(parm);
						}
					}
					try (AlgorithmDAI algoDAO = theDb.makeAlgorithmDAO())
					{
						Logger.instance().info(
							"Running computation " + comp.getName() + " modelRunId=" + comp.getModelRunId()
								+ ", with parms: ");
						for (Iterator dcpit = comp.getParms(); dcpit.hasNext();)
						{
							DbCompParm dcp = dcpit.next();
							CTimeSeries cts = runme.getTimeSeries(dcp.getSiteDataTypeId(), dcp.getInterval(),
								dcp.getTableSelector(), comp.getModelRunId());
							TimeSeriesIdentifier tsid = cts != null ? cts.getTimeSeriesIdentifier() : null;

							Logger.instance().info(
								"   "
									+ dcp.getRoleName()
									+ ":sdi="
									+ dcp.getSiteDataTypeId()
									+ ",intv="
									+ dcp.getInterval()
									+ ",tsel="
									+ dcp.getTableSelector()
									+ ",modId="
									+ dcp.getModelId()
									+ (cts == null ? " no existing TimeSeries" : " Existing TS with " + cts.size()
										+ " values in it ")
									+ (tsid == null ? "(no tsid)" : "and ts_id key=" + tsid.getKey() + " "
										+ tsid.getUniqueString()));
						}
						// run inputs through computation
						comp.setAlgorithm(algoDAO.getAlgorithmById(comp.getAlgorithmId()));
						comp.prepareForExec(theDb);
						comp.apply(runme, theDb);
					}
					catch (DbCompException e)
					{
						if (!isCancelled())
						{
							showError(module + " DbCompException in " + "runButtonPressed() " + e.getMessage());
						}
						continue;
					}
					catch (DbIoException e)
					{
						if (!compExecutionWorker.isCancelled())
						{
							showError(module + " DbIOException in " + "runButtonPressed() " + e.getMessage());
						}
						continue;
					}
					catch (NoSuchObjectException e)
					{
						if (!compExecutionWorker.isCancelled())
						{
							showError(module + " Cannot read Algorithm in " + "runButtonPressed() " + e.getMessage());
						}
						continue;
					}
					progress.incDone();
					setProgress(progress.getPercentDone());
					// Get all outputs & add outputs to total lists;
					for (DbCompParm parm : outputParms)
					{
						boolean found = false;
						for (CTimeSeries cts : runme.getAllTimeSeries())
							if (cts.getSDI() == parm.getSiteDataTypeId()
								&& TextUtil.strEqual(cts.getInterval(), parm.getInterval())
								&& TextUtil.strEqual(cts.getTableSelector(), parm.getTableSelector()))
							{
								publish(cts);
								outputs.add(cts);
								Logger.instance().info(
									"After running, Found output sdi=" + cts.getSDI() + ", size=" + cts.size()
										+ ", units=" + cts.getUnitsAbbr());
								found = true;
								break;
							}
						if (!found)
							Logger.instance().info("No time series found for output role " + parm.getRoleName());
					}
				}
				return outputs;
			}

			@Override
			protected void process(List chunks)
			{
				for (CTimeSeries cts : inputs)
				{
					if (!both.contains(cts))
					{
						both.add(cts);
					}
				}

				for (CTimeSeries cts : chunks)
				{
					if (both.contains(cts))
						continue;

					myoutputs.add(cts);

					TimeSeriesDAI timeSeriesDAO = theDb.makeTimeSeriesDAO();
					try
					{
						// We need to get meta data to display the axes.
						// But if units are already defined for an output, don't change
						// them.
						String oldUnits = cts.getUnitsAbbr();
						timeSeriesDAO.fillTimeSeriesMetadata(cts);
						if (oldUnits != null && !oldUnits.equalsIgnoreCase("unknown"))
							cts.setUnitsAbbr(oldUnits);
						Logger.instance().info(
							"After fill - Output TS: " + cts.getDisplayName() + ", nsamps=" + cts.size() + ", units="
								+ cts.getUnitsAbbr());
					}
					catch (DbIoException e)
					{
						Logger.instance().warning(
							module + " DbIoException in " + "runButtonPressed() filling outputs " + e.getMessage());
						continue;
					}
					catch (BadTimeSeriesException e)
					{
						Logger.instance().warning(
							module + " BadTimeSeriesException in " + "runButtonPressed() filling outputs "
								+ e.getMessage());
						continue;
					}
					finally
					{
						timeSeriesDAO.close();
					}

					both.add(cts);
					plotDataOnChart(both, inputs.size());
					timeSeriesTable.setInOut(inputs, myoutputs);
				}
			}

			@Override
			protected void done()
			{
				runButton.setEnabled(true);
				setProgress(100);
				// Stop trace logger and remove from pipeline
				traceLogger.setDialog(null);
				Logger.setLogger(originalLogger);
				cancelExecutionButton.setEnabled(false);
				saveButton.setEnabled(true);
				plotDataOnChart(both, inputs.size());
				timeSeriesTable.setInOut(inputs, myoutputs);
			}
		};
		compExecutionWorker.addPropertyChangeListener(event -> updateProgress(event));
		progressBar.setStringPainted(true);
		progressBar.setString("Running");
		progressBar.setValue(0);
		saveButton.setEnabled(false);
		cancelExecutionButton.setEnabled(true);
		compExecutionWorker.execute();
		needToSave = true;
	}

	private void updateProgress(PropertyChangeEvent event)
	{
		if ("progress".equals(event.getPropertyName()))
		{
			int value = (Integer)event.getNewValue();
			progressBar.setValue(value);
			if (value == 100)
			{
				progressBar.setString("done");
			}
			else if (progress != null)
			{
				progressBar.setString(String.format("%d of %d", progress.getDone(), progress.getTotal()));
			}
		}
	}

	private JScrollPane getTable()
	{
		JScrollPane myscrollpane = new JScrollPane();
		myscrollpane.setPreferredSize(new Dimension(600, 400));
		timeSeriesTable = new TimeSeriesTable(theDb);
		timeSeriesTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		myscrollpane.add(timeSeriesTable);
		myscrollpane.setViewportView(timeSeriesTable);
		myscrollpane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		return myscrollpane;
	}

	private JPanel closePanel()
	{
		JPanel closePanel = new JPanel(new FlowLayout());
		JButton closeButton = new JButton();
		closeButton.setText(closeButtonLabel);
		closeButton.setPreferredSize(new java.awt.Dimension(100, 25));
		closeButton.setName("closeButton");
		closeButton.addActionListener(new java.awt.event.ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				doClose();
			}
		});
		closePanel.add(closeButton);

		return closePanel;
	}

	private boolean doClose()
	{
		if (compExecutionWorker != null && !compExecutionWorker.isDone())
		{
			int r = JOptionPane.showConfirmDialog(this, cancelComputationExecution);
			if (r == JOptionPane.CANCEL_OPTION || r == JOptionPane.NO_OPTION)
			{
				return false;
			}
			else
			{
				compExecutionWorker.cancel(true);
				needToSave = false;
			}
		}
		if ((myoutputs != null) && (myoutputs.size() != 0) && needToSave)
		{
			int r = JOptionPane.showConfirmDialog(this, saveCompOutput);
			switch(r)
			{
				case JOptionPane.CANCEL_OPTION:
				{
					return false;
				}
				case JOptionPane.YES_OPTION:
				{
					saveCompOutput();
					break;
				}
				default:
				{
					needToSave = false;
				}
			}
		}
		// depending on the mode that this GUI was started, we'll call
		// System exit or not
		if (!standAloneMode)
		{
			compEditParent.setRunCompGUIUp(false);
			dispose();
		}
		else
		{
			dispose();
			if (exitOnClose)
			{
				/**
				 * TODO: This shouldn't be necassary and Java will exit when the last non-daemon
				 *  thread exits and we should rely on that behavior instead of forcing a System.exit
				 */ 
				System.exit(0);
			}
		}
		return true;
	}

	/**
	 * This is used when this GUI is launch from the Comp Edit GUI
	 * 
	 * @return true or false
	 */
	public boolean closeFromParent()
	{
		return doClose();
	}

	private void saveButtonPressed()
	{
		if ((myoutputs == null) || (myoutputs.size() == 0))
		{
			JOptionPane.showMessageDialog(this, "No computation output !",
				labels.getString("RunComputationsFrame.frameTitle"), JOptionPane.WARNING_MESSAGE);
			return;
		}

		int r = JOptionPane.showConfirmDialog(this, "Are you sure to save the computation output ?",
			labels.getString("RunComputationsFrame.frameTitle"), JOptionPane.YES_NO_OPTION);
		if (r == JOptionPane.YES_OPTION)
		{
			saveCompOutput();
			needToSave = false;
		}

	}

	private void saveCompOutput()
	{
		TimeSeriesDAI timeSeriesDAO = theDb.makeTimeSeriesDAO();
		try
		{
			for (CTimeSeries myseries : myoutputs)
			{
				try
				{
					Logger.instance().info("Calling saveTimeSeries, size=" + myseries.size());
					timeSeriesDAO.saveTimeSeries(myseries);
				}
				catch (DbIoException ex)
				{
					String msg = module + " Can not write Time Series to the " + "Database "
						+ ex.getMessage();
					showError(msg);
				}
				catch (BadTimeSeriesException ex)
				{
					String msg = module + " Can not write Time Series to the " + "Database "
						+ ex.getMessage();
					Logger.instance().failure(msg);
				}
			}
		}
		finally
		{
			timeSeriesDAO.close();
		}
	}

	private void selectButtonPressed()
	{
		if (theDb != null)
		{
			Vector myvector = new Vector();

			if (computationsListDialog == null)
			{
				computationsListDialog = new ComputationsListDialog(this, theDb, getRunCompFrametester());
			}

			launchDialog(computationsListDialog);
			myvector = computationsListDialog.getComputations();

			if (computationsListDialog.wasOK())
			{
				mytable.fill(myvector);
			}
		}
	}

	private void removeButtonPressed()
	{
		mytable.remove();
	}

	/** For testing purpose */
	public static void main(String[] args)
	{
		JFrame myframe = new CompRunGuiFrame(true);
		myframe.setVisible(true);
		myframe.setSize(400, 400);
	}

	/**
	 * @return the runCompFrametester
	 */
	public RunComputationsFrameTester getRunCompFrametester()
	{
		return runCompFrametester;
	}

	/**
	 * @param runCompFrametester
	 *            the runCompFrametester to set
	 */
	public void setRunCompFrametester(RunComputationsFrameTester runCompFrametester)
	{
		this.runCompFrametester = runCompFrametester;

	}

	private static class ProgressState
	{
		private int total;
		private int done;

		public ProgressState(int total)
		{
			this.total = total;
			this.done = 0;
		}

		public void incDone()
		{
			done++;
		}

		public int getTotal()
		{
			return total;
		}

		public int getDone()
		{
			return done;
		}

		public int getPercentDone()
		{
			return (100*done)/total;
		}
	}
}

class ComputationsTable extends JTable
{
	ComputationsTableModel mymodel = new ComputationsTableModel();

	ComputationsTable()
	{
		this.setModel(mymodel);
	}

	public String getColumnName(int column)
	{
		if (column == 0)
		{
			return CompRunGuiFrame.labels.getString("RunComputationsFrame.computationName");
		}
		else if (column == 1)
		{
			return CompRunGuiFrame.description;
		}
		else
		{
			return "";
		}
	}

	public void fill(Vector newvector)
	{
		mymodel.fill(newvector);
	}

	public void remove()
	{
		int[] rows = this.getSelectedRows();
		for (int pos = 0; pos < rows.length; pos++)
		{
			mymodel.remove(rows[pos]);
		}
	}

}

class ComputationsTableModel extends AbstractTableModel
{
	public Vector myvector = new Vector();

	public String getColumnName(int column)
	{
		if (column == 0)
		{
			return CompRunGuiFrame.labels.getString("RunComputationsFrame.computationName");
		}
		else if (column == 1)
		{
			return CompRunGuiFrame.description;
		}
		else
		{
			return "";
		}
	}

	public Object getValueAt(int y, int x)
	{
		if (x == 0)
		{
			return myvector.get(y).getName();
		}
		else if (x == 1)
		{
			return myvector.get(y).getComment();
		}
		else
		{
			return null;
		}
	}

	public int getColumnCount()
	{
		return 2;
	}

	public int getRowCount()
	{
		return myvector.size();
	}

	public void remove(int row)
	{
		if (row >= 0 && row < myvector.size())
		{
			myvector.remove(row);
			this.fireTableDataChanged();
		}
	}

	public void fill(Vector newvector)
	{
		myvector.addAll(newvector);
		// myvector = newvector;
		this.fireTableDataChanged();
	}
}

class ComputationsListDialog extends JDialog
{
	private ComputationsListPanel compListPanel;
	private boolean ok = false;
	private RunComputationsFrameTester mytester;

	ComputationsListDialog(TopFrame owner, TimeSeriesDb mydb, RunComputationsFrameTester frameTester)
	{
		super(owner, CompRunGuiFrame.selectFromListLabel, true);
		this.mytester = frameTester;

		boolean noCompToken = mytester.getNoCompFilterToken();
		compListPanel = new ComputationsListPanel(mydb, !noCompToken, true, owner);

		compListPanel.setIsDialog(true);
		this.setLayout(new BorderLayout());
		this.add(compListPanel, BorderLayout.CENTER);
		this.setSize(750, 600);
		JPanel buttonPanel = new JPanel();
		JButton okButton = new JButton(CompRunGuiFrame.okButtonLabel);// "  OK  "
		JButton cancelButton = new JButton(CompRunGuiFrame.cancelButtonLabel);
		okButton.addActionListener(new java.awt.event.ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				okButtonPressed();
			}
		});

		cancelButton.addActionListener(new java.awt.event.ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				cancelButtonPressed();
			}
		});

		buttonPanel.setLayout(new FlowLayout());
		buttonPanel.add(okButton);
		buttonPanel.add(cancelButton);
		this.add(buttonPanel, BorderLayout.SOUTH);

	}

	/**
	 * @return the mytester
	 */
	public RunComputationsFrameTester getMytester()
	{
		return mytester;
	}

	/**
	 * @param mytester
	 *            the mytester to set
	 */
	public void setMytester(RunComputationsFrameTester mytester)
	{
		this.mytester = mytester;
	}

	private void okButtonPressed()
	{
		ok = true;
		setVisible(false);
		dispose();
	}

	private void cancelButtonPressed()
	{
		setVisible(false);
		dispose();
	}

	public boolean wasOK()
	{
		return ok;
	}

	public Vector getComputations()
	{
		if (ok)
		{
			return compListPanel.getSelectedComputations();
		}
		else
		{
			return new Vector();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy